Skip to content

Store copy of data state before applying and after clearing filter

I have a simple app that calls an API, returns the data (as an array of objects), sets a data state, and populates a few charts and graphs.

const loadData = async () => {
    const url = 'https://my-api/api/my-api';
    const response = await fetch(url);
    const result = await response.json();


After setting the data, the data state is sent to every component and everything is populated. I created a filters pane that can filter the existing, populated data (for example, a gender filter that filters the data on the selected gender). What I did, and it’s obviously wrong, is created an onChange handler that filters the data to the selected gender then uses the setData (sent as a prop; also the state variable, data) to set the filtered data. When I clear the filter, the original, non-filtered data is replaced by the filtered data so the original data is lost.

const genderFilterHanlder = (e) => {
    const filteredData = data.filter(x => x.gender ===;

I tried creating an intermediary state the preserves the original data then upon clearing the filters, it sets the data (setData) to the original. But this breaks when I have a filter that allows you to choose multiple values (like multiple languages; I can choose one language, clear it successfully, but if I choose two languages, then clear one, it breaks as the data is now the first chosen filter data).

How would I go about this?


I’d leave data itself alone and have a separate filteredData state member that you set using an effect:

const [filteredData, setFilteredData] = useState(data);
const [filter, setFilter] = useState("");
// ...
useEffect(() => {
    const filteredData = filter ? data.filter(/*...apply filter...*/) : data;
}, [filter, data]); // <=== Note our dependencies
// ...
// ...render `filteredData`, not `data`...

Then your change handler just updates filter (setFilter(/*...the filter...*/)).

That way, any time the filter changes, or any time data changes, the data gets filtered and rendered.

Live Example:

const { useState, useEffect } = React;

const Child = ({data}) => {
    const [filteredData, setFilteredData] = useState(data);
    const [filter, setFilter] = useState("");

    useEffect(() => {
        if (!filter) {
        const lc = filter.toLocaleLowerCase();
        const filteredData = filter
            ? data.filter(element => element.toLocaleLowerCase().includes(lc))
            : data;
    }, [filter, data]); // <=== Note our dependencies
    return <div>
        <input type="text" value={filter} onChange={({currentTarget: {value}}) => setFilter(value)} />
            { => <li key={element}>{element}</li>)}

const greek = [
const initialData = greek.slice(0, 4);
const Example = () => {
    const [data, setData] = useState(initialData);

    useEffect(() => {
        const handle = setInterval(() => {
            setData(data => {
                if (data.length < greek.length) {
                    return [, greek[data.length]];
                return data;
        }, 800);
        return () => {
    }, []);

    return <Child data={data} />;

ReactDOM.render(<Example />, document.getElementById("root"));
<div id="root"></div>

<script src=""></script>
<script src=""></script>