4

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
      ])
1

3 Answers 3

12
if (!categories.includes(category) === false) {

is a double negative. Remove the === false.

An alternative is to use a Set for uniqueness since it automatically discards duplicates. Typically, sets offer fast lookup time, but for small amounts of data the performance is probably not much different than array with includes since both structures are rebuilt on every render.

Here's a toy example of using Set:

const App = () => {
  const [categories, setCategories] = React.useState(new Set());
  const [item, setItem] = React.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}
      />&nbsp;
      <button onClick={addCategory}>Add Item</button>
      <ul>{[...categories].map(e => <li key={e}>{e}</li>)}</ul>
    </div>
  );
};

ReactDOM.createRoot(document.querySelector("#app"))
  .render(<App />);
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div id="app"></div>

And an includes example:

const Example = () => {
  const [categories, setCategories] = React.useState([]);
  const [item, setItem] = React.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}
      />&nbsp;
      <button onClick={addCategory}>Add Item</button>
      <ul>{categories.map(e => <li key={e}>{e}</li>)}</ul>
    </div>
  );
};

ReactDOM.createRoot(document.querySelector("#app"))
  .render(<Example />);
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div id="app"></div>

Sign up to request clarification or add additional context in comments.

Comments

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) })
}

Comments

1

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)) {...}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.