我有两个reducer动作,我想一个接一个地分派。第一个修改状态,然后第二个使用修改后的状态的一部分进行另一次修改。困难在于调用第二个调度时,它仍然具有旧的过时状态,因此无法正确更新该状态。
下面是一个示例(也可以在此处找到https://codesandbox.io/s/react-usereducer-hqtc2),其中有一个对话列表以及一个被视为“活动”对话的注释:
import React, { useReducer } from "react";
const reducer = (state, action) => {
switch (action.type) {
case "removeConversation":
return {
...state,
conversations: state.conversations.filter(
c => c.title !== action.payload
)
};
case "setActive":
return {
...state,
activeConversation: action.payload
};
default:
return state;
}
};
export default function Conversations() {
const [{ conversations, activeConversation }, dispatch] = useReducer(
reducer,
{
conversations: [
{ title: "James" },
{ title: "John" },
{ title: "Mindy" }
],
activeConversation: { title: "James" }
}
);
function removeConversation() {
dispatch({ type: "removeConversation", payload: activeConversation.title });
dispatch({ type: "setActive", payload: conversations[0] });
}
return (
<div>
Active conversation: {activeConversation.title}
<button onClick={removeConversation}>Remove</button>
<ul>
{conversations.map(conversation => (
<li key={conversation.title}>{conversation.title}</li>
))}
</ul>
</div>
);
}
在这里,当我单击“删除对话”按钮时,我要删除活动对话,然后将活动对话设置为列表顶部的对话。但是,在这里,当第一个调度从列表中删除会话时,第二个调度将active设置为conversations[0]
,该会话仍包含删除的值(因为状态尚未更新)。结果,即使活动会话已从列表中删除,它仍将活动会话保持为以前的状态。
我可能可以将逻辑合并为一个动作,然后在其中执行所有操作(删除对话,并将活动设置为一个活动),但是理想情况下,我希望让我的reducer动作尽可能地承担一个责任。
有什么方法可以使第二个调度调用具有最新版本的状态,从而不会发生这种问题?
答案 0 :(得分:0)
如果您将useEffect()
当作setState
的第二个参数(来自基于类的组件),可能会有所帮助。
如果要使用最新状态进行操作,请使用useEffect()
,该状态会在状态更改时出现:
const {
useState,
useEffect
} = React;
function App() {
const [count, setCount] = useState(0);
const decrement = () => setCount(count-1);
const increment = () => setCount(count+1);
useEffect(() => {
console.log("useEffect", count);
}, [count]);
console.log("render", count);
return (
<div className="App">
<p>{count}</p>
<button onClick={decrement}>-</button>
<button onClick={increment}>+</button>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render( < App / > , rootElement);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.6/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script>
<div id="root"></div>