【问题标题】:How can I create a helper method that takes a param to filter data from a redux store?如何创建一个辅助方法,该方法需要一个参数来过滤来自 redux 存储的数据?
【发布时间】:2020-07-09 17:23:25
【问题描述】:

在我的 react/redux/redux-thunk 应用程序中,我有一个管理状态的 reducer,其中包含类似于以下内容的列表:

state = {
  stuff: [
    {
      id: 1,
      color: "blue",
      shape: "square"
    },
    {
      id: 2,
      color: "red",
      shape: "circle"
    },
    {
      id: 3,
      color: "yellow",
      shape: "square"
    },
  ]
};

我想创建可以在我的应用程序中使用的辅助函数,它根据传递给函数的参数从商店返回过滤后的内容列表。例如:

getStuffByShape("square");  // returns array with stuff 1 and 3
getStuffByColor("red"); // returns array with stuff 2

我了解到我可以创建一个可以根据需要导入到不同文件中的单例存储,但不建议这样做。我目前没有进行任何服务器端渲染,但我不想在未来限制我的选择。

我已经阅读了有关创建选择器和 reselect 包的信息,但这些示例仅显示了采用状态参数的函数,我不清楚是否可以传入额外的任意参数。

我可以将状态作为参数从连接的组件传递,但我可能想在其他地方使用这些函数,例如其他辅助函数。

【问题讨论】:

    标签: redux react-redux redux-thunk reselect


    【解决方案1】:

    你可以创建一个parameterized selector,我的首选方法是你可以记忆的咖喱:

    const { Provider, useSelector } = ReactRedux;
    const { createStore, applyMiddleware, compose } = Redux;
    const { createSelector } = Reselect;
    
    const initialState = {
      stuff: [
        {
          id: 1,
          color: 'blue',
          shape: 'square',
        },
        {
          id: 2,
          color: 'red',
          shape: 'circle',
        },
        {
          id: 3,
          color: 'yellow',
          shape: 'square',
        },
      ],
    };
    const reducer = (state) => state;
    //helper
    const createFilterBy = (field, value) => (item) =>
      value ? item[field] === value : true;
    //selectors
    const selectStuff = (state) => state.stuff;
    const createSelectFiltered = (filterFn) =>
      createSelector([selectStuff], (stuff) =>
        stuff.filter(filterFn)
      );
    const createSelectByColor = (color) =>
      createSelector(
        [createSelectFiltered(createFilterBy('color', color))],
        (x) => x
      );
    const createSelectByShape = (shape) =>
      createSelector(
        [createSelectFiltered(createFilterBy('shape', shape))],
        (x) => x
      );
    //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 List = React.memo(function List({ items }) {
      return (
        <ul>
          {items.map((item, index) => (
            <li key={index}>{JSON.stringify(item)}</li>
          ))}
        </ul>
      );
    });
    const SelectList = React.memo(function SelectList({
      label,
      value,
      setter,
      options,
    }) {
      return (
        <label>
          {label}
          <select
            value={value}
            onChange={({ target: { value } }) =>
              setter(value === 'all' ? undefined : value)
            }
          >
            <option value="all">all</option>
            {options.map((option) => (
              <option key={option} value={option}>
                {option}
              </option>
            ))}
          </select>
        </label>
      );
    });
    const colors = ['blue', 'red', 'yellow'];
    const shapes = ['square', 'circle'];
    const App = () => {
      const [color, setColor] = React.useState();
      const [shape, setShape] = React.useState();
      const selectByColor = React.useMemo(
        () => createSelectByColor(color),
        [color]
      );
      const selectByShape = React.useMemo(
        () => createSelectByShape(shape),
        [shape]
      );
      const byColor = useSelector(selectByColor);
      const byShape = useSelector(selectByShape);
      return (
        <div>
          <div>
            <SelectList
              label="color"
              value={color}
              setter={setColor}
              options={colors}
            />
            <SelectList
              label="shape"
              value={shape}
              setter={setShape}
              options={shapes}
            />
          </div>
          <div>
            <h4>color</h4>
            <List items={byColor} />
            <h4>shape</h4>
            <List items={byShape} />
          </div>
        </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>

    【讨论】:

    • 感谢您抽出宝贵时间发布此答案!这很有帮助,但是,我想确保我了解接线。看起来你的例子中的createSelectByColor 相当于我原来的帖子中的getStuffByColor("red");,对吗?无论我在哪里导入它,我都必须将它包装在 useSelector() 中以传递状态? selectByColor 只是使用记忆的中间步骤,还是以某种方式传递状态信息也是必不可少的?
    • @HectorO getStuffByColor("red"); 在我的代码中是 createSelectByColor()('red')createSelectByColor() 在 useMemo 中,因此组件有自己的选择器。 here 解释了它是如何工作的以及为什么这样做。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-07-07
    • 2016-02-10
    • 2020-10-11
    • 2019-08-27
    • 1970-01-01
    • 1970-01-01
    • 2018-09-24
    相关资源
    最近更新 更多