Javascript 反应状态+;使用效果+;事件提供过时状态
我试图使用带有ReactJavascript 反应状态+;使用效果+;事件提供过时状态,javascript,reactjs,codemirror,eventemitter,react-hooks,Javascript,Reactjs,Codemirror,Eventemitter,React Hooks,我试图使用带有Reactuseffect和useState的事件发射器,但它总是获取初始状态,而不是更新状态。如果我直接调用事件处理程序,即使使用setTimeout,它也可以工作 如果我将该值传递给useffect()2参数,它将使其工作,但这会导致每次值更改时(由击键触发)都重新订阅事件发射器 我做错了什么?我尝试了useState、useRef、useReducer、和useCallback,但都无法获得任何工作 这是复制品: import React,{useState,useffec
useffect
和useState
的事件发射器,但它总是获取初始状态,而不是更新状态。如果我直接调用事件处理程序,即使使用setTimeout
,它也可以工作
如果我将该值传递给useffect()
2参数,它将使其工作,但这会导致每次值更改时(由击键触发)都重新订阅事件发射器
我做错了什么?我尝试了useState
、useRef
、useReducer
、和useCallback
,但都无法获得任何工作
这是复制品:
import React,{useState,useffect}来自“React”;
从“react-codemirror2”导入{控制为CodeMirror};
导入“codemirror/lib/codemirror.css”;
从“事件”导入EventEmitter;
设ee=neweventemitter();
const initialValue=“初始值”;
功能应用程序(道具){
const[value,setValue]=使用状态(initialValue);
//应在初始服务器加载后以及Codemirror输入更改时获取最新值。
const handleEvent=(消息,数据)=>{
info(“事件处理程序中的值:”,值);
//这一行只是为了演示这个问题。如果我们想在这个事件中修改DOM,我们会调用一些setState函数,并以一种反应友好的方式重新渲染。
document.getElementById(“结果”).innerHTML=value;
};
//在组件创建时从服务器获取值(模拟)
useffect(()=>{
设置超时(()=>{
setValue(“来自服务器的值”);
}, 1000);
}, []);
//订阅组件创建时的事件
useffect(()=>{
ee.on(“某些事件”,handleEvent);
return()=>{
ee.关闭(手动通风);
};
}, []);
返回(
{
设置值(新值);
}}
/>
{/*下面的所有内容仅用于演示问题。实际上,事件将来自此组件外部的其他源。*/}
{
ee.发射(“某些事件”);
}}
>
EventEmitter(不工作)
);
}
导出默认应用程序代码>更新了答案
问题不在于挂钩。初始状态值被关闭并传递给EventEmitter,并被反复使用
在handleEvent
中直接使用状态值不是一个好主意。相反,我们需要在发出事件时将它们作为参数传递
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);
const [isReady, setReady] = useState(false);
// Should get the latest value
function handleEvent(value, msg, data) {
// Do not use state values in this handler
// the params are closed and are executed in the context of EventEmitter
// pass values as parameters instead
console.info("Value in event handler: ", value);
document.getElementById("result").innerHTML = value;
}
// Get value from server on component creation (mocked)
useEffect(() => {
setTimeout(() => {
setValue("value from server");
setReady(true);
}, 1000);
}, []);
// Subscribe to events on component creation
useEffect(
() => {
if (isReady) {
ee.on("some_event", handleEvent);
}
return () => {
if (!ee.off) return;
ee.off(handleEvent);
};
},
[isReady]
);
function handleClick(e) {
ee.emit("some_event", value);
}
return (
<React.Fragment>
<CodeMirror
value={value}
options={{ lineNumbers: true }}
onBeforeChange={(editor, data, newValue) => {
setValue(newValue);
}}
/>
<button onClick={handleClick}>EventEmitter (works now)</button>
<div id="result" />
</React.Fragment>
);
}
export default App;
import React,{useState,useffect}来自“React”;
从“react-codemirror2”导入{控制为CodeMirror};
导入“codemirror/lib/codemirror.css”;
从“事件”导入EventEmitter;
设ee=neweventemitter();
const initialValue=“初始值”;
功能应用程序(道具){
const[value,setValue]=使用状态(initialValue);
const[isReady,setReady]=useState(false);
//应获取最新的值
函数handleEvent(值、消息、数据){
//不要在此处理程序中使用状态值
//参数是关闭的,并在EventEmitter的上下文中执行
//将值作为参数传递
info(“事件处理程序中的值:”,值);
document.getElementById(“结果”).innerHTML=value;
}
//在组件创建时从服务器获取值(模拟)
useffect(()=>{
设置超时(()=>{
setValue(“来自服务器的值”);
setReady(真);
}, 1000);
}, []);
//订阅组件创建时的事件
使用效果(
() => {
如果(isReady){
ee.on(“某些事件”,handleEvent);
}
return()=>{
如果(!ee.off)返回;
ee.关闭(手动通风);
};
},
[伊斯雷迪]
);
函数handleClick(e){
ee.emit(“某个事件”,值);
}
返回(
{
设置值(新值);
}}
/>
EventEmitter(现在工作)
);
}
导出默认应用程序;
下面是一个工作的值
在事件处理程序中过时,因为它从定义它的闭包中获取其值。除非我们在每次value
更改时重新订阅一个新的事件处理程序,否则它将不会获得新的值
解决方案1:对发布效果设置第二个参数[value]
。这会使事件处理程序获得正确的值,但也会使效果在每次击键时再次运行
解决方案2:使用ref
在组件实例变量中存储最新的值。然后,每次值
状态发生变化时,只会更新此变量。在事件处理程序中,使用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);
};
看起来该页面上还有其他一些解决方案也可以使用。非常感谢@Dinesh的帮助。useCallback应该在这里工作
import React, { useState, useEffect, useCallback } from "react";
import PubSub from "pubsub-js";
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
const handler = (msg, data) => {
console.info("Value in event handler: ", value);
document.getElementById("result").innerHTML = value;
};
const handleEvent = useCallback(handler, [value]);
// Get value from server on component creation (mocked)
useEffect(() => {
setTimeout(() => {
setValue("value from server");
}, 1000);
}, []);
// Subscribe to events on component creation
useEffect(() => {
PubSub.subscribe("some_event", handleEvent);
return () => {
PubSub.unsubscribe(handleEvent);
};
}, [handleEvent]);
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);
}}
/>
<button
onClick={() => {
ee.emit("some_event");
}}
>
EventEmitter (works)
</button>
<button
onClick={() => {
PubSub.publish("some_event");
}}
>
PubSub (doesnt work)
</button>
<button
onClick={() => {
setTimeout(() => handleEvent(), 100);
}}
>
setTimeout (works!)
</button>
<div id="result" />
</React.Fragment>
);
}
export default App;
import React,{useState,useffect,useCallback}来自“React”;
从“PubSub js”导入PubSub;
从“react-codemirror2”导入{控制为CodeMirror};
导入“codemirror/lib/codemirror.css”;
从“事件”导入EventEmitter;
设ee=neweventemitter();
const initialValue=“初始值”;
功能应用程序(道具){
const[value,setValue]=使用状态(initialValue);
//应获取最新的值
常量处理程序=(消息,数据)=>{
info(“事件处理程序中的值:”,值);
document.getElementById(“结果”).innerHTML=value;
};
const handleEvent=useCallback(处理程序,[value]);
//在组件创建时从服务器获取值(模拟)
useffect(()=>{
设置超时(()=>{
setValue(“来自服务器的值”);
}, 1000);
}, []);
//订阅组件创建时的事件
useffect(()=>{
订阅(“某些事件”,handleEvent);
return()=>{
PubSub.unsubscribe(handleEvent);
};
},[handleEvent]);
useffect(()=>{
ee.on(“某些事件”,handleEvent);
return()=>{
ee.关闭(手动通风);
};
}, []);
返回(
{