I've got some buttons in my navbar when clicked return a filtered state. These objects are then used to render cards in my UI.
<div className="filter-buttons">
<button className="filter-btn" onClick={() => setFilter("nature")}>Nature</button>
<button className="filter-btn" onClick={() => setFilter("tech")}>Tech</button>
<button className="filter-btn" onClick={() => setFilter("education")}>Education</button>
</div>
I'm using redux toolkit library for my state. I pull in all blog objects and store it into a variable. I have a useEffect hook that runs everytime a button is clicked to try and reset the state and filter all the objects again.
const blogsCopy = JSON.stringify(useSelector((state) => state.blog));
const allBlogs = useSelector((state) => state.blog);
const myBlogs = useSelector((state) => state.user.blogs);
const [filter, setFilter] = useState("");
const originalState = useMemo(() => JSON.parse(blogsCopy), []);
useEffect(() => {
setNavFilterLoading(true);
let filteredBlogs = [];
const keys = Object.keys(allBlogs);
if (filter.startsWith("nat")) {
// reset the state to the original state
dispatch(setWholeState(originalState));
console.log("original state", originalState);
filteredBlogs = keys.filter(key => allBlogs[key].tags.some(tag => tag.startsWith("nat"))).map((key, index) => ({index, ...allBlogs[key]})
);
} else if (filter.startsWith("tech")) {
filteredBlogs = keys.filter(key => allBlogs[key].tags.some(tag => tag.startsWith("tech"))).map((key, index) => ({index, ...allBlogs[key]})
);
} else if (filter.startsWith("edu")) {
filteredBlogs = keys.filter(key => allBlogs[key].tags.some(tag => tag.startsWith("edu"))).map((key, index) => ({index, ...allBlogs[key]})
);
} else {
filteredBlogs = keys.map((key, index) => ({index, ...allBlogs[key]})
);
}
dispatch(setWholeState(filteredBlogs));
setNavFilterLoading(false);
}, [filter]);
Issue with this implementation is, the object gets mutated and I'm not able to reset the state again. If I clicked on the nature button, it will return 3 objects. If one of the three includes a 'tech' tag. It will filter the three objects and return one object. I want it to reset the whole state to an initial copy to properly filter it out. Not filter what has been filtered.
CodePudding user response:
I think the use of useEffect
to save filteredBlogs
to the store might not be necessary here since filter
seems to be for local use only.
Assuming that the goal is to filter data for conditional display, and the data retrieved from store works as expected, perhaps there is no need to mutate the data, instead a local filteredBlogs
can be created within the component.
A rough example could be something like:
const blogsCopy = JSON.stringify(useSelector((state) => state.blog));
const allBlogs = useSelector((state) => state.blog);
const myBlogs = useSelector((state) => state.user.blogs);
const [filter, setFilter] = useState("");
const filteredBlogs = allBlogs.filter((item) =>
filter ? item.tags.some((tag) => tag.startsWith(filter)) : true
);
Local filteredBlogs
can then be used for output with filteredBlogs.map()
and it would be updated when the filter
state change.
Perhaps the buttons could also be changed to save the shorter value as filter
, for simpler logic:
<div className="filter-buttons">
<button className="filter-btn" onClick={() => setFilter("")}>Clear all</button>
<button className="filter-btn" onClick={() => setFilter("nat")}>Nature</button>
<button className="filter-btn" onClick={() => setFilter("tech")}>Tech</button>
<button className="filter-btn" onClick={() => setFilter("edu")}>Education</button>
</div>