jamesco
jamesco

Reputation: 83

React – can't filter array properly

I'm trying to add filter functionality to my project. From a list of entries with different languages, an array of all languages is created. The user should be able to click a language and have React filter to only show entries with that language. However, I can't seem to update the entries state properly when running the filterIt function. If I console.log entries after running setEntries(filtered), the result is the same.

const Archive = () => {
  const [entries, setEntries] = useState([]);
  let dreamFilter = [];

  //get all entries from firestore
  useEffect(() => {
    const unsubscribe = firebase
      .firestore()
      .collection("entries")
      .onSnapshot((snapshot) => {
        const newEntries = snapshot.docs.map((doc) => ({
          id: doc.id,
          ...doc.data(),
        }));
        setEntries(newEntries);
      });
    return () => unsubscribe();
  }, []);

  //after entries are loaded, create filter of all languages for 'dream'
  if (entries.length > 0) {
    const categoryMap = Object.values(entries)
      .reduce(
        (concatedArr, item) => concatedArr.concat(Object.entries(item)),
        []
      )
      .reduce((result, [category, values]) => {
        result[category] = result[category] || [];
        result[category] = result[category].concat(values);
        return result;
      }, {});

      dreamFilter = categoryMap.dream.filter(
        (item, pos) => categoryMap.dream.indexOf(item) === pos
      );
  }

  function filterIt(value) {
    const filtered = entries.filter(entry => ({
      ...entry,
      filtered: entry.dream.includes(value)
    }));
    console.log("filtered results = " + JSON.stringify(filtered));
    setEntries(filtered);
    return filtered;
  }

  return (
    <>
      <Navigation />
      <ul>
        {dreamFilter.map((language, i) => (
          <li key={i}>
            <a href="/" onClick={(value) => { filterIt(value); value.preventDefault(); }}>{language}</a>
          </li>
        ))}
      </ul>
      <ArchiveContainer>
        {entries.map((entry) => (
          <div key={entry.id}>
            <a href={"/entry/" + entry.id}>
              <h5>{entry.id}</h5>
              <p>{entry.dream}</p>
            </a>
          </div>
        ))}
      </ArchiveContainer>
    </>
  );
}

Upvotes: 0

Views: 562

Answers (2)

Vishwadeep Kapoor
Vishwadeep Kapoor

Reputation: 104

In functional component you can't update states directly. assuming here you are trying to add filtered: true/false also in entries array. array.filter is for adding or removing the object from array by returning true/false.

function filterIt(value) {
 setEntries(entryList => entryList.map(item => ({
   ...item,
   filtered: item.dream.includes(value)
 })));
}

Upvotes: 0

Yash Joshi
Yash Joshi

Reputation: 2774

Filter method should return either true or false. Read more here. If you want to convert one array to an array of another object, then use map.

You need to modify filter method to be like this:

const filtered = entries.filter(entry => entry.dream.includes(value));

Upvotes: 1

Related Questions