当 redux 状态改变时,React-Redux 在屏幕上渲染组件两次 [英] React-Redux rendering components twice on screen when redux state changes
问题描述
我正在尝试制作具有回复功能的嵌套评论组件.当没有添加新评论时,代码工作正常,但是每当我为新评论发送操作时,组件都会在屏幕上呈现两次(给出,重复键警告).
const App = () =>{const dispatch = useDispatch();const data = useSelector((gstate) => gstate.commentsReducer.comments);const struct_data = 评论中间件(数据);//根据我的需要构建数据的实用函数.useEffect(() => dispatch(commentsAction({comments: dummy_comments })), [派遣,]);控制台日志(数据);返回 (<div className="root-app"><AddComment pid={null}/><ViewComment data={struct_data}/>
);};
减速器
const commentsReducer = (state = { comments: [] }, action) =>{开关(动作.类型){案例评论":返回 {...状态,评论:action.payload,};默认:返回状态;}};导出默认的commentsReducer;
实用功能
const commentsMiddleware = (comments) =>{常量哈希 = {};comment.forEach((c) => (hash[c.id] = c));for(让 c 评论){if (c.pid !== null) {const p = hash[c.pid];if (p.children == null) p.children = [];p.children.push(c);}}评论 = comments.filter((c) => c.pid === null);回复评论;};导出默认评论中间件;
你的代码有几个问题:
- commentsMiddleware 正在改变 redux 状态
- 应该使用选择器对评论进行分组
如果您不想变异并且不想在添加一条评论后重新计算所有内容,您可以执行以下操作:
const { Provider, useDispatch, useSelector } = ReactRedux;const { createStore, applyMiddleware, compose } = Redux;const { createSelector } = 重新选择;const { useState, memo } = React;const id = ((num) => () => num++)(1);常量初始状态 = {注释: [],};//动作类型const ADD_COMMENT = 'ADD_COMMENT';//动作创建者const addComment = (comment) =>({类型:ADD_COMMENT,有效载荷:评论,});const reducer = (state, { type, payload }) =>{如果(类型 === ADD_COMMENT){返回 {...状态,评论:[有效负载,...state.comments],};}返回状态;};//选择器const selectComments = (state) =>状态.评论;const selectCommentsMap = createSelector([选择评论],(评论) =>评论.减少((评论,评论)=>评论集(评论 ID,评论),新地图()));const recursiveUpdate = (更新,nestedMap) =>{const recur = (更新) =>{NestedMap.set(updated.id, 更新);if (updated.id === 'root') {返回;}const parent = nestedMap.get(updated.pid);const newParent = {...父母,孩子: parent.children.map((child) =>child.id === 更新.id ?更新:孩子),};返回 recur(newParent);};返回递归(更新,nestedMap);};const addNewComment = (comment,nestedMap) =>{评论 = { ...评论,孩子:[] };NestedMap.set(comment.id, comment);const parent = nestedMap.get(comment.pid);常量更新父 = {...父母,孩子们:[评论,...parent.children],};递归更新(更新父级,nestedMap);};const selectGroupedComments = (() => {constnestedMap = new Map([['root', { id: 'root', children: [] }],]);返回创建选择器([选择评论地图],(currentMap) =>{[...currentMap.entries()].forEach(([id, comment]) => {//添加注释到nestedComments如果 (!nestedMap.get(id)) {addNewComment(评论,nestedMap);}});//我让你弄清楚如何删除评论//[...nestedMap.entries()].forEach//检查 id 是否不在 curentMap 中返回nestedMap.get('root').children;});})();//IIFE//使用 redux 开发工具创建 storeconst composeEnhancers =窗口.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ||撰写;const store = createStore(减速器,初始状态,composeEnhancers(applyMiddleware(() => (next) => (action) =>下一个(动作))));const AddComment = memo(function AddComment({pid = 'root',}) {const [text, setText] = useState('');const dispatch = useDispatch();返回 (<div><输入类型=文本"值={文本}onChange={(e) =>setText(e.target.value)}/><按钮onClick={() =>{dispatch(addComment({ text, id: id(), pid }));setText('');}}>向 {pid} 添加评论按钮>
);});const Comment = memo(function Comment({ comment }) {const { id, text, children } = 注释;console.log('渲染注释:', id);返回 (<li><h3>评论:{text},ID:{id}<Comments key={id} comments={children}/><AddComment pid={id}/>);});const 评论 = 备忘录(功能评论({ 评论}){返回 (<div>{Boolean(comments.length) &&(<ul>{comments.map((comment) => (<评论键={comment.id} 评论={comment}/>))})}
);});const App = () =>{const 评论 = useSelector(selectGroupedComments);返回 (<div><评论comments={comments}/><AddComment pid="root"/>
);};ReactDOM.render(<提供者商店={商店}><应用程序/></提供者>,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>