我如何创建一个带有参数的辅助方法来过滤来自 redux 存储的数据?

How can I create a helper method that takes a param to filter data from a redux store?

在我的 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"
    },
  ]
};

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

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

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

我读过有关创建选择器和 reselect 包的内容,但示例仅显示了采用状态参数的函数,我不清楚是否可以传入一个额外的任意参数。

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

您可以创建一个 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>