0

So I am having an issue with filtering functionality using react hooks where I have a set of filters with tags associated to the filters. A user clicks on a filter and then the if the company has a tag associated with it then it will only show those filters.
Now when they click on a filter I need that filter to go into the array then from that array I want to do the company filtering and setCompanies - however I get an error as you cant setCompanies inside useEffect.

In this example there would be a button with tag node - would then need to filter all the companies that have node associated with them. If they unclick the filter then it needs to show all filters hence the filters.length === 0 piece of code.

How would you set this up properly so it does not error?

const company = [{name:'google', tags:['javascript', 'node']}, {name:'facebook', tags:['javascript', 'ruby']}]

function App() {
  const [filters, setFilter] = useState([])
  const [companies, setCompanies] = useState(company)


  useEffect(() => {
filterCompany()
  }, [filters, companies]);


  function addOrRemoveFilter(item) {
    if (filters.includes(item)) {
      const filteredItems = filters.filter(filteredItem => filteredItem !== item)
      setFilter([...filteredItems])

    } else {
      setFilter([...filters, item])
    }
  }

  function filterCompany() {
    const res = companies.filter(company => {
      return filters.every(filter => {
        return company.tags.includes(filter)
      })
    })
    const listOfCompanies = filters.length === 0 ? company : res

    setCompanies([...listOfCompanies])
  }


  return (
    <div className="App">



      <div className="columns is-multiline filters is-centered">

        {data.values.map((item, index) => (
          <HomeFilters key={index} data={item} onFilterClick={(item) => addOrRemoveFilter(item)} />
        ))}
      </div>
      <div className="columns is-multiline is-centered ">
        {companies.map((item, index) => (
          <Companies key={index} data={item} />
        ))}
      </div>


    </div>


  );
}

export default App;

3 Answers 3

1

If you are filtering data then you can just create another function to get the filtered data and use that to render it. This way you don't need to update state and it will automatically re-render when filters array changes.

Example:

function getFilteredCompanies() {
  const res = companies.filter(company => {
    return filters.every(filter => {
      return company.tags.includes(filter);
    });
  });

  return filters.length === 0 ? company : res;
}

Render:

{getFilteredCompanies().map((item, index) => <span key={index}>{item.name}</span>)}
Sign up to request clarification or add additional context in comments.

Comments

0

You are calling filterCompany() before it is declared.

Just move this function above useEffect

transform

useEffect(() => {
  filterCompany()
}, [filters, companies]);

into

useEffect(() => {
  filterCompany()
}, []);

1 Comment

I get Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect eit
0

Use useMemo to calculate derivable data rather than update it in useState:

  const [filters, setFilter] = useState([])

  function addOrRemoveFilter(item) {
    if (filters.includes(item)) {
      const filteredItems = filters.filter(filteredItem => filteredItem !== item)
      setFilter([...filteredItems])

    } else {
      setFilter([...filters, item])
    }
  }

  function filterCompany() {
    const res = company.filter(comp => {
      return filters.every(filter => {
        return comp.tags.includes(filter)
      })
    })
    return filters.length === 0 ? company : res
  }

  const companies = useMemo(() => filterCompany, [filters]);

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.