根据id重新选择createSelector



我在React项目中使用了重新选择库。

我已经创建了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如果不是,如何正确处理

这里的问题是,作为HOC的react-redux具有connect(...)功能,该功能允许选择器为(state, props) => ...,而useSelector只传递(state) => ...

为了允许仍然传递道具,您可以实现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(..)使用相同的选择器

这不是真正正确的方法来选择特定的iduseSelector钩子不允许向选择器传递超过state的内容。

一个迂回的解决方案可以是将specific后的id也存储到状态中,并选择该状态。不利的一面是,post在初始渲染时将不可用,您需要调度(从useEffect钩子或回调(一个操作来存储所需的postid

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

相关内容

  • 没有找到相关文章