Denis
Denis

Reputation: 121

Filter by category React Redux

I have a data which I receive from Redux Store, also I have categories by which I need to filter data by clicking on appropriate category button. How can I implement filter using Redux? items from database has output like this:

items = [{
    id: 0
    name: "example1"
    price: 15
    category: 0
    
  },
  {
    id: 1
    name: "example2"
    price: 15
    category: 1
  }
]

categories has output:

categories = ["one", "two"]

This is my Component:

export default function Home() {
  const itemData = useSelector((state) => state.main.items);
  const loader = useSelector((state) => state.load.loading);

  const [activeCategory, setActiveCategory] = useState(null);

  const dispatch = useDispatch();

  useEffect(() => {
    dispatch(fetchItems());
  }, [dispatch]);

  if (loader) {
    return <Spinner />;
  }

  return (
    <div className="container">
      <div className="content__top">
        <div className="categories">
          <ul>
            <li
              className={activeCategory === null ? 'active' : ''}
              onClick={() => setActiveCategory(null)}>
              All
            </li>
            {categories &&
              categories.map((category, index) => (
                <li
                  key={index}
                  className={activeCategory === index ? 'active' : ''}
                  onMouseDown={() => dispatch(filterByCategory(itemData, category))}
                  onClick={() => setActiveCategory(index)}>
                  {category}
                </li>
              ))}
          </ul>
        </div>
      </div>
      <h2 className="content__title">Items</h2>
      <div className="content__items">
        {itemData && itemData.map((item) => <Content key={item._id} {...item} />)}
      </div>
    </div>
  );
}

This is my Action:

export const FETCH_ITEMS = 'FETCH_ITEMS';
export const FILTER_BY_CATEGORY = 'FILTER_BY_CATEGORY';

export const fetchItems = () => {
  return (dispatch) => {
    dispatch(showLoader());
    axios.get('http://localhost:4000/').then((response) => {
      const items = response.data;
      dispatch({
        type: FETCH_ITEMS,
        payload: items,
      });
      dispatch(hideLoader());
    });
  };
};

export const filterByCategory = (items, category) => {
  return {
    type: FILTER_BY_CATEGORY,
    category,
    // some kind of filter code
  };
};

And Reducer

const initialState = {
  items: [],
};

export const fetchReducer = (state = initialState, action) => {
  switch (action.type) {
    case actions.FETCH_ITEMS:
      return { ...state, items: action.payload };
    case actions.FILTER_BY_CATEGORY:
      return { ...state, filtered: action.category, category }; // Not sure about it
    default:
      return state;
  }
};

Upvotes: 2

Views: 750

Answers (1)

HMR
HMR

Reputation: 39250

Here is a working example using selectors:

const { Provider, useSelector } = ReactRedux;
const { createStore, applyMiddleware, compose } = Redux;
const { createSelector } = Reselect;

const initialState = {
  items: [
    {
      id: 0,
      name: 'example1',
      price: 15,
      category: 0,
    },
    {
      id: 1,
      name: 'example2',
      price: 15,
      category: 1,
    },
    {
      id: 2,
      name: 'example3',
      price: 88,
      category: 1,
    },
  ],
};
const reducer = (state) => state;
//selectors
const selectItems = (state) => state.items;
const selectCategories = createSelector(
  [selectItems],
  (items) => [
    ...new Set(
      items.map(({ category }) => category).sort()
    ),
  ]
);
const createSelectFilteredItems = (filterFunction) =>
  createSelector([selectItems], (items) =>
    items.filter(filterFunction)
  );
//creating store with redux dev tools
const composeEnhancers =
  window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(
  reducer,
  initialState,
  composeEnhancers(
    applyMiddleware(() => (next) => (action) =>
      next(action)
    )
  )
);
const App = () => {
  const categories = useSelector(selectCategories);
  const [
    selectedCategory,
    setSelectedCategory,
  ] = React.useState();
  const selectFilteredItems = React.useMemo(
    () =>
      createSelectFilteredItems(({ category }) =>
        selectedCategory === undefined
          ? true
          : category === selectedCategory
      ),
    [selectedCategory]
  );
  const filteredItems = useSelector(selectFilteredItems);
  return (
    <div>
      <label>
        select category
        <select
          value={selectedCategory}
          onChange={({ target: { value } }) =>
            setSelectedCategory(
              value === 'all' ? undefined : Number(value)
            )
          }
        >
          <option value="all">all</option>
          {categories.map((category) => (
            <option key={category} value={category}>
              {category}
            </option>
          ))}
        </select>
      </label>

      <ul>
        {filteredItems.map((item) => (
          <li key={item.id}>{JSON.stringify(item)}</li>
        ))}
      </ul>
    </div>
  );
};

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.5/redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/7.2.0/react-redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/reselect/4.0.0/reselect.min.js"></script>
<div id="root"></div>

Upvotes: 3

Related Questions