Denis
Denis

Reputation: 65

Data are erasing after filter it

The problem is that if I am applying filter, filteredList will be updated based on the filter if I would like to filter it again, it will give me nothing back.

const [filteredList, setFilteredList] = useState([]);
  const [searchquery, setSearchquery] = useState("");


     useEffect(() => {
        fetch("api/device/getall")
          .then((res) => res.json())
          .then(
            (result) => {
              setIsLoaded(true);
              setFilteredList(result);
            },
            (error) => {
              setIsLoaded(true);
              setError(error);
            }
          );
      }, []);


 const FilterSearchQuery = (filteredData) => {
    const filteredDevice = filteredData.filter(
      (item) =>
        item.deviceName
          .toString()
          .toLowerCase()
          .indexOf(searchquery.toLowerCase()) > -1 ||
    );
    return filteredDevice;
  };
    
    useEffect(() => {
        var filteredData = FilterDevicelocation(filteredList);
        setFilteredList(filteredData);
      }, [searchquery]);

Upvotes: -1

Views: 57

Answers (1)

Drew Reese
Drew Reese

Reputation: 203457

Issue

Filtering an array is necessarily a reducing action. The issue is that you are reducing the state that is the "source of truth", and once reduced can never be un-reduced unless you fetch the entire state again. The issue is caused by the useEffect hook that filters and updates the filteredList state when the query value updates.

useEffect(() => {
  var filteredData = FilterDevicelocation(filteredList);
  setFilteredList(filteredData); // <-- removed array entries
}, [searchquery]);

Solution

filteredList is your "source of truth", don't mutate it once it is set. The filtered result you want to render is what is considered derived state, meaning it is "state" that is easily derived from other state and/or props. In this case the filtered "state" is derived from the current filteredList state array and the current searchQuery string values. Do the filtering inline prior to rendering it.

Example:

const [list, setList] = useState([]);
const [searchQuery, setSearchQuery] = useState("");

useEffect(() => {
  fetch("api/device/getall")
    .then((res) => res.json())
    .then((result) => {
      setList(result);
    })
    .catch((error) => {
      setError(error);
    })
    .finally(() => {
      setIsLoaded(true);
    });
}, []);

const filteredList = useMemo(() => list.filter(
  (item) =>
    item.deviceName
      .toString()
      .toLowerCase()
      .includes(searchQuery.toLowerCase())
), [list, searchQuery]);

// use the derived filteredList from here out

Upvotes: 3

Related Questions