Вот простое приложение для реагирования, которое перечислит некоторые фиктивные данные и может быть отфильтровано по категориям (в данном случае по идентификатору пользователя) путем включения / выключения кнопки.
FilterBtn.js
import React, { useContext, useState } from "react";
import DataContext from "../../context/dataContext";
import "./FilterBtn.css";
const FilterBtn = ({ category }) => {
const { currentCategory, setCategory, filterData } = useContext(DataContext);
const [isFilter, setIsFilter] = useState(false);
/* The idea here is to toggle the filter by click and then send both category
and isFilter in order to trigger the filter dispatch in State
*/
const onClick = () => {
/* Not sure on how to properly make this work since setIsFilter is asynchronous
and isFilter will still be false. I read that useEffect or useCallback could work
but I am not sure if they are efficient at all.
*/
// setIsFilter(prevState => !prevState) also doesn't seem to work.
setIsFilter(!isFilter);
/* This is to set the currentCategory in order to know which button is being clicked on
and will apply the highlighted class on it if true
*/
if (currentCategory !== category) {
setCategory(category);
} else {
setCategory(null);
}
// Here I invert isFilter so that it will be sent to state as "true"
filterData(category, !isFilter);
/* Note that the toggle logic is still flawed, and will not toggle the data as expected if the
toggle switch constantly between buttons
*/
};
return (
// I need to know which category it is currently being toggled on in order to change the class
<button
value={category}
onClick={onClick}
className={currentCategory === category ? "highlighted" : ""}
>
User {category}
</button>
);
};
export default FilterBtn;
DataState.js
import React, { useReducer } from "react";
import DataContext from "./dataContext";
import DataReducer from "./dataReducer";
import axios from "axios";
import types from "./types";
const { GET_DATA, FILTER_DATA, SET_CATEGORY } = types;
const DataState = ({ children }) => {
const initialState = {
data: [],
currentCategory: null
};
const [state, dispatch] = useReducer(DataReducer, initialState);
// To set the currentCategory being clicked on
const setCategory = (category) => {
dispatch({
type: SET_CATEGORY,
payload: category
});
};
// The filter works by sending category parameter to the backend in order to filter the data and then send it back again to react
// There is probably a way to filter without having to fire another request to backend
const filterData = async (userId, filter) => {
// Some simple logic in which filter will only trigger if category exists and filter is true
if (userId && filter) {
const res = await axios.get(
`https://jsonplaceholder.typicode.com/posts?userId=${userId}`
);
dispatch({
type: FILTER_DATA,
payload: res.data
});
} else {
// Get the localStorage data, which stores the initial unfiltered data
const savedData = JSON.parse(localStorage.getItem("data"));
dispatch({
type: GET_DATA,
payload: savedData
});
}
};
const fetchData = async () => {
const res = await axios.get("https://jsonplaceholder.typicode.com/posts");
// Save the unfiltered data in localStorage in order to be able to get them again if filter is false
localStorage.setItem("data", JSON.stringify(res.data));
dispatch({
type: GET_DATA,
payload: res.data
});
};
return (
<DataContext.Provider
value={{
data: state.data,
fetchData,
setCategory,
filterData,
currentCategory: state.currentCategory
}}
>
{children}
</DataContext.Provider>
);
};
export default DataState;
DataReducer.js
import types from "./types";
const { GET_DATA, FILTER_DATA, SET_CATEGORY } = types;
export default (state, action) => {
switch (action.type) {
case GET_DATA:
return {
...state,
data: action.payload
};
case FILTER_DATA:
return {
...state,
data: action.payload
};
case SET_CATEGORY:
return {
...state,
currentCategory: action.payload
};
default:
return state;
}
};
Я использовал localStorage для реализации этой функции (сохранять исходные неотфильтрованные данные в localStorage во время выборки и получать данные из localStorage, когда переключатель выключен). Кажется, что этот код можно улучшить.
Вот рабочие коды и ящик:
https://codesandbox.io/s/agitated-butterfly-xbm1n