【问题标题】:Filter by category React Redux按类别过滤 React Redux
【发布时间】:2020-07-08 12:26:27
【问题描述】:

我有一个从 Redux Store 收到的数据,还有我需要通过单击适当的类别按钮来过滤数据的类别。如何使用 Redux 实现过滤器? 数据库中的项目具有如下输出:

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

类别有输出:

categories = ["one", "two"]

这是我的组件:

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>
  );
}

这是我的行动:

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
  };
};

和减速器

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;
  }
};

【问题讨论】:

  • 过滤后的数据是所有数据的计算结果,为什么不用选择器过滤呢?看起来您想在 store 中不必要地存储一个可以计算的计算值。
  • HMR,很好的提示。所以我需要首先使用选择器,然后 - 渲染项目?或者我可能需要使用重新选择库之类的东西?
  • 我肯定会建议using reselect

标签: reactjs redux


【解决方案1】:

这是一个使用选择器的工作示例:

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>

【讨论】:

  • HMR,一切都很好,但我有一个小问题 - 我的项目默认情况下不呈现,但在单击类别按钮后 - 项目呈现。如何使呈现默认类别,例如所有项目?
  • @Denis 你用的是什么代码?我更改了答案,以便能够显示未过滤的列表。
  • 我稍微修改了你的代码——好吧,我应该检查一下自己)
  • 我想用分类作为li,而不是select
猜你喜欢
  • 2019-12-07
  • 2021-05-18
  • 1970-01-01
  • 2021-11-15
  • 1970-01-01
  • 1970-01-01
  • 2020-02-08
  • 1970-01-01
相关资源
最近更新 更多