Фильтр состояния данных при переключении по категориям

Вот простое приложение для реагирования, которое перечислит некоторые фиктивные данные и может быть отфильтровано по категориям (в данном случае по идентификатору пользователя) путем включения / выключения кнопки.

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

0

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *