Search code examples
javascriptreactjsuse-state

Why is my variable emptied in React.js when I use setState()


I am creating a simple list of cards and I need to filter it according to which tags are selected. I have:

function Homepage() {
  const [cards, setCards] = useState([]);
  const [filteredCards, setFilteredCards] = useState([]);
  let filteredTags = [];

The tags are buttons with onClick={toggleSingleTag} and then I have this function:

function toggleSingleTag(e) {
  if (!filteredTags.includes(e.target.innerHTML)){
    filteredTags.push(e.target.innerHTML);
  } else {
    filteredTags = filteredTags.filter(function(elem){
      return elem != e.target.innerHTML;
    })
  }
  // setFilteredCards(filteredTags)
}

If I console.log filteredTags at the end of the function, I get the expected result (tags are included or removed from the list). But when I try to add setFilteredCards() (removing comment on the line) and console.log the values, I get that:

  1. filteredTags only ever includes the last clicked item (n), regardless of what was clicked at n-1. So it looks like the list is emptied everytime before the function is triggered, otherwise if n == n-1 the list should be empty.
  2. filteredCards includes the n-1 element and nothing else.

What am I doing wrong? How do I fix this?


Solution

  • filteredTags is declared and defined as a new empty array with each component render:

    let filteredTags = [];
    

    If you want the value to persist across renders, that's what state is for:

    const [filteredTags, setFilteredTags] = useState([]);
    

    Just like with your other state values, this will define it as an empty array on the first render and allow you to update state with each render. For example, instead of this:

    filteredTags.push(e.target.innerHTML);
    

    You would update the state value:

    setFilteredTags([...filteredTags, e.target.innerHTML]);
    

    And instead of this:

    filteredTags = filteredTags.filter(function(elem){
      return elem != e.target.innerHTML;
    })
    

    You would update the state value:

    setFilteredTags(filteredTags.filter(function(elem){
      return elem != e.target.innerHTML;
    }));
    

    Basically, plain local variables are useful as helpers only for that render. Anything that should be maintained across renders belongs in state.