根据 id 重新选择 createSelector
我在我的 React 项目中使用 reselect 库。
我创建了
Posts
选择器,它工作正常。这是代码
// selectors.ts
const getPosts = (state: RootState) => state.posts.posts;
export const getPostsSelector = createSelector(getPosts, (posts) => posts);
我在我的页面上这样调用它
// SomePage.tsx
const posts = useSelector(getPostsSelector);
现在我需要通过 id 获取帖子。我想这样做:
// selectors.ts
const getPostDetail = (state: RootState) =>
state.posts.posts.entities[ID];
export const getPostsById = createSelector(
getPostDetail,
(detail) => detail
);
并在页面上调用它:
const PostDetail = useSelector(getPostsById);
我有两个问题:
- 获取单个帖子的方法正确吗?
- 如果是,如何传递帖子的 ID,如果不是,如何以正确的方式处理?
这里的问题是,
react-redux
作为 HOC 具有
connect(...)
函数,该函数允许选择器为
(state, props) => ...
,而
useSelector
仅传递
(state) => ...
。 ...
。
为了仍然允许传递 props,您可以实现
useSelectorWithProps
:
function useSelectorWithProps (selector, props, deps, equalityFn) {
const selectorFn = useCallback(state => selector(state, props), deps)
return useReduxSelector(
selectorFn,
equalityFn
)
}
这样您就可以执行以下操作:
// selectors.js
const selectPosts = (state) => state.posts.posts;
const selectPostById = createSelector(
selectPosts,
(_, props) => props.id,
(posts, id) => posts[id]
)
// Or make a utility function for selecting the prop
const selectProp = (key, orDefault) => (_, props) => (props[key] || orDefault)
const selectPostById = createSelector(
selectPosts,
selectProp('id'),
(posts, id) => posts[id]
)
// but don't do this, as it will de-optimize the memoization of reselect.
// `{id: 1} !== {id: 1}` and thus causes it to recalculate the selector
// (Keyword: referential equality)
const selectPostById = createSelector(
selectPosts,
(_, props) => props,
(posts, { id }) => posts[id]
)
// component.js
function Component(props) {
const value = useSelectorWithProps(
selectPostById,
{ id: props.id }, // pass an object
[ props.id ] // pass dependencies of the props-object
)
}
这是我在我工作的项目中实现它的方式。
这允许您的完整“选择器逻辑”仍然存在于您的选择器中,从而更容易测试,并且不必在钩子中编写更少的粘合代码。
它还允许您对
connect(...)
和
useSelectorWithProps(..)
使用相同的选择器。
按特定
id
进行选择实际上
不
是正确的方法。
useSelector
钩子不允许将除
state
之外的内容传递给选择器。
一种迂回的解决方案是将
特定
帖子
id
也存储到状态中并选择它。缺点是帖子在初始渲染时将不可用,并且您需要分派(从
useEffect
钩子或回调)一个操作来存储您想要的帖子
id
。
const getPostById = createSelector(
[getPostsSelector, getPostId],
(posts, id) => posts.find(post => post.id === id);
);
用法:
const postById = useSelector(getPostById);
...
dispatch(setPostId(postId)); // store the id
由于您无法创建选择器来直接按
id
返回特定帖子,因此我建议创建一个选择器,该选择器返回帖子的派生状态对象,以便更快地进行查找,即地图或对象。
示例:
const postDetailMap = createSelector(
[getPosts],
posts => posts.reduce((posts, post) => ({
...posts,
[post.id]: post,
}), {}),
);
用法:
const postsMap = useSelector(postDetailMap);
const specificPost = postMap[postId];
您也许能够将其抽象为自定义钩子不过。
const useGetPostById = id => {
const postsMap = useSelector(postDetailMap);
return postsMap[id];
}
用法:
const post = useGetPostById(postId);