'How do I not include duplicate values in a React state array?
I want to make sure that I can't push a duplicated value into an array in a React state. The duplicated value is still going in the array though.
I have tried using .includes but it is not working.
const categoryCheck = (category) => {
let categories = [...this.state.categories]
if (this.state.categories === undefined) {
return
}
console.log(categories, category)
console.log(!categories.includes(category))
if (!categories.includes(category) === false) {
categories.push(category)
console.log('new category', categories)
}
}
input: 'cat'
Expected result: ['cat']
Actual result: ['cat', 'cat']
UPDATE: This is my function and this is how I call it. Thanks for all the help!
uniqueCategories = category => {
//makes sure that there are no duplicate categories in array
if (category === undefined) {
return;
}
let categories = new Set(category);
categories = Array.from(categories);
console.log(categories);
return categories;
};
I call it in another function like this:
this.setState({
categories: this.uniqueCategories([
...this.state.categories,
categoryInput
])
Solution 1:[1]
if (!categories.includes(category) === false) {
is a double negative. Remove the === false.
An alternative is to use a Set for uniqueness. Typically, sets offer fast lookup time and automatically reject duplicates, but for small amounts of data the performance is probably no better than array with includes which is rebuilt on every render.
Here's a toy example of using Set:
const {useState} = React;
const Example = () => {
const [categories, setCategories] = useState(new Set());
const [item, setItem] = useState("");
const addCategory = e => {
if (item.trim()) {
setCategories(prevState => new Set(prevState).add(item.trim()));
}
};
return (
<div>
<input
onChange={e => setItem(e.target.value)}
value={item}
/>
<button onClick={addCategory}>Add Item</button>
<ul>{[...categories].map(e => <li key={e}>{e}</li>)}</ul>
</div>
);
};
ReactDOM.render(<Example />, document.querySelector("#app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>
<div id="app"></div>
And an includes example:
const {useState} = React;
const Example = () => {
const [categories, setCategories] = useState([]);
const [item, setItem] = useState("");
const addCategory = e => {
const trimmed = item.trim();
if (trimmed && !categories.includes(trimmed)) {
setCategories(prevState => prevState.concat(trimmed));
}
};
return (
<div>
<input
onChange={e => setItem(e.target.value)}
value={item}
/>
<button onClick={addCategory}>Add Item</button>
<ul>{categories.map(e => <li key={e}>{e}</li>)}</ul>
</div>
);
};
ReactDOM.render(<Example />, document.querySelector("#app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>
<div id="app"></div>
Solution 2:[2]
You can use Set in ES6
const categoryCheck = (category) => {
if (this.state.categories === undefined) {
return
}
let categories = new Set(this.state.categories)
categories.add(category)
this.setState({ categories: Array.from(categories) })
}
Solution 3:[3]
Your logic is reversed:
if (!categories.includes(category) === false) {...}
Will return true if the item is not in the array. Just use this:
if (!categories.includes(category)) {...}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | roel |
| Solution 3 | Jack Bashford |
