React 规范化Redux store场景下应在哪里查询状态?
你可以采用展示组件+容器组件分离的成熟模式,同时兼顾两类场景的需求,不需要牺牲任何一方的体验。
具体实现方案
将同一个组件拆为两层:
- 纯展示层:无任何Redux依赖,所有渲染所需数据都通过props传入,专门用于测试、Storybook场景
- 容器层:仅负责对接Redux Store获取数据,把取到的参数透传给展示组件,业务开发时直接调用容器组件即可
示例代码
Comment组件拆分
// 纯展示组件:无store依赖,参数清晰,方便测试和写Storybook export const Comment = ({ username, date, title, content }) => { return ( <div> <p>{username}@{date}</p> <em>{title}</em> <p>{content}</p> </div> ); }; // 容器组件:仅负责对接store,业务代码直接调用这个版本 export const ConnectedComment = ({ commentId }) => { const comment = useSelector(state => selectComment(state, commentId)); return <Comment {...comment} />; };
PostView组件拆分
// 纯展示组件 export const PostView = ({ title, content, comments }) => { return ( <> <h1>{title}</h1> <p>{content}</p> {comments?.map(comment => <Comment key={comment.id} {...comment} />)} </> ); }; // 容器组件 export const ConnectedPostView = ({ postId }) => { const post = useSelector(state => selectPost(state, postId)); // 批量获取关联评论,不要在循环内调用useSelector const comments = useSelector(state => post.comments?.map(commentId => selectComment(state, commentId)) ?? [] ); return <PostView {...post} comments={comments} />; };
方案优势
- 业务开发体验和你提到的「新方案」完全一致,仅需要传入id即可,组件自动获取所有关联数据,数据变更时仅对应组件重渲染的性能优势也完全保留
- 测试和Storybook场景直接调用纯展示组件,不需要mock Redux Store,也保留了直接修改props调试的能力
- 彻底规避了你尝试的「混合方案」中「在循环内调用Hook」的语法错误
可选优化
如果担心批量查询评论导致不必要的重渲染,可以用Reselect创建带缓存的聚合Selector,仅当Post或者关联的Comment数据变更时才返回新值:
import { createSelector } from '@reduxjs/toolkit'; const selectPostWithComments = createSelector( [selectPost, selectAllComments], (post, allComments) => { if (!post) return null; return { ...post, comments: post.comments?.map(id => allComments.find(comment => comment.id === id)) ?? [] }; } );
如果组件逻辑非常简单,不想拆分两个组件,也可以做参数兼容:传入id就从store取数据,否则直接用传入的props,不过这种方式不如拆分清晰,优先推荐分层方案。
内容的提问来源于stack exchange,提问作者Newlukai
相关产品推荐
相关产品推荐

