我正在尝试将事件发射器与React useEffect和一起使用useState,但是它始终获取初始状态而不是更新状态。如果我直接调用事件处理程序(即使使用),也可以使用setTimeout。
useEffect
useState
setTimeout
如果我将值传递给useEffect()第二个参数,它将使其起作用,但是,每当值更改时(这是击键触发的),这都会导致事件发射器重新订阅。
useEffect()
我究竟做错了什么?我试过useState,useRef,useReducer,和useCallback,并不能得到任何工作。
useRef
useReducer
useCallback
这是复制品:
import React, { useState, useEffect } from "react"; import { Controlled as CodeMirror } from "react-codemirror2"; import "codemirror/lib/codemirror.css"; import EventEmitter from "events"; let ee = new EventEmitter(); const initialValue = "initial value"; function App(props) { const [value, setValue] = useState(initialValue); // Should get the latest value, both after the initial server load, and whenever the Codemirror input changes. const handleEvent = (msg, data) => { console.info("Value in event handler: ", value); // This line is only for demoing the problem. If we wanted to modify the DOM in this event, we would instead call some setState function and rerender in a React-friendly fashion. document.getElementById("result").innerHTML = value; }; // Get value from server on component creation (mocked) useEffect(() => { setTimeout(() => { setValue("value from server"); }, 1000); }, []); // Subscribe to events on component creation useEffect(() => { ee.on("some_event", handleEvent); return () => { ee.off(handleEvent); }; }, []); return ( <React.Fragment> <CodeMirror value={value} options={{ lineNumbers: true }} onBeforeChange={(editor, data, newValue) => { setValue(newValue); }} /> {/* Everything below is only for demoing the problem. In reality the event would come from some other source external to this component. */} <button onClick={() => { ee.emit("some_event"); }} > EventEmitter (doesnt work) </button> <div id="result" /> </React.Fragment> ); } export default App;
这是具有相同代码的代码沙箱App2:
App2
https://codesandbox.io/s/ww2v80ww4l
App组件具有3种不同的实现-EventEmitter,pubsub-js和setTimeout。仅setTimeout有效。
App
为了阐明我的目标,我只是希望其中handleEvent的值在所有情况下都与Codemirror值匹配。单击任何按钮时,应显示当前的代码镜像值。而是显示初始值。
handleEvent
value在事件处理程序中是过时的,因为它从定义它的闭包中获取其值。除非每次value更改都重新订阅新的事件处理程序,否则它将不会获得新值。
value
解决方案1:对发布效果进行第二个论证[value]。这使事件处理程序获得正确的值,但也使效果在每次击键时再次运行。
[value]
解决方案2:使用ref将最新的存储value在组件实例变量中。然后,产生一个效果,每当value状态更改时,该效果仅更新此变量。在事件处理程序中,请使用ref,而不是value。
ref
const [value, setValue] = useState(initialValue); const refValue = useRef(value); useEffect(() => { refValue.current = value; }); const handleEvent = (msg, data) => { console.info("Value in event handler: ", refValue.current); };
https://reactjs.org/docs/hooks-faq.html#what-can-i-do-if-my-effect- dependencies-change-too-oftenten
该页面上似乎还有其他一些解决方案也可能起作用。非常感谢@Dinesh的帮助。