我创建了一个自定义钩子
const useMessageStorage = () => {
const [messages, setMessages] = useState([]);
const addMessages = (newMessage) => {
setMessages(oldMessages => [...oldMessages, newMessage)]);
}
const clear = () => {
setMessages([]);
}
return { clear, addMessages }
}
在消费者组件内部,我想做一些类似的事情
const { clear, addMessages } = useMessageStorage();
...
clear() // clear before adding new messages
addMessage('new message')
以上不起作用,因为两个调用都是异步的。但我想在某些特定场景中添加新消息之前清除所有消息。如何解决这个问题?我考虑过使用 useRef
,但由于竞争条件我害怕使用它。
答案 0 :(得分:0)
React 状态更新是异步处理的,这是真的,但它们也是按照它们入队的顺序处理的。由于您的addMessages
回调使用功能状态更新,它将从前一个状态更新,不是它被排入队列的渲染周期的状态。
// clear()
setMessages([]); // "draft" state: []
// addMessage(newMessage)
setMessages((oldMessages) => [ // updated from "draft" state: [newMessage]
...oldMessages,
newMessage,
]);
您的代码确实有效。
const useMessageStorage = () => {
const [messages, setMessages] = React.useState([]);
const addMessages = (newMessage) => {
setMessages((oldMessages) => [...oldMessages, newMessage]);
};
const clear = () => {
setMessages([]);
};
return { clear, messages, addMessages };
};
function App() {
const [message, setMessage] = React.useState("");
const { clear, messages, addMessages } = useMessageStorage();
const addMessage = () => {
if (message) {
addMessages(message);
setMessage("");
}
};
const clearAndAddMessage = () => {
if (message) {
clear();
addMessages(message);
setMessage("");
}
};
return (
<div className="App">
<label>
Message:
<input
type="text"
id="message"
onChange={(e) => setMessage(e.target.value)}
value={message}
/>
</label>
<button type="button" onClick={addMessage}>
Add Message
</button>
<button type="button" onClick={clearAndAddMessage}>
Clear & Add Message
</button>
<ul>
{messages.map((message, i) => (
<li key={i}>{message}</li>
))}
</ul>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(
<App />,
rootElement
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
<div id="root" />