给出以下示例
#app div {
padding: 10px;
border: 1px solid;
margin: 10px 0;
}
div:focus {
background: red;
}
div:focus:before {
content: "focused";
display: block;
}
<div id="app"></div>
<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>
<script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
<script type="text/babel">
let { useState, useRef, Fragment } = React;
let App = () => {
let [, forceUpdate] = useState();
let [num, setNum] = useState(0);
let history = useRef('null');
let keyPressHandler = () => {
history.current = num;
// just to force an update
forceUpdate(Date.now());
};
return (
<Fragment>
<button onClick={()=>{setNum(Date.now())}}>update state</button>
<div tabindex="0" onKeyPress={keyPressHandler}>this div has a keypress listener. <br/>for now whatever key you press it will save the state in a ref, that changes on the button click. <br/>Click the button to change the state then focus this div then press any key to save the state in the ref</div>
<div>State : {num}</div>
<div>History: {history.current}</div>
</Fragment>
);
};
ReactDOM.render(<App />, document.querySelector("#app"));
</script>
现在我将把按键监听器移到窗口,应该做同样的事情。
#app div {
padding: 10px;
border: 1px solid;
margin: 10px 0;
}
<div id="app"></div>
<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>
<script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
<script type="text/babel">
let { useEffect, useState, useRef, Fragment } = React;
let App = () => {
let [, forceUpdate] = useState();
let [num, setNum] = useState(0);
let history = useRef('null');
useEffect(() => {
window.addEventListener("keypress", keyPressHandler);
}, []);
let keyPressHandler = () => {
console.log('called keyPressHandler')
history.current = num;
// just to force an update
forceUpdate(Date.now());
};
return (
<Fragment>
<button onClick={()=>{setNum(Date.now())}}>update state</button>
<div>State : {num}</div>
<div>History: {history.current}</div>
</Fragment>
);
};
ReactDOM.render(<App />, document.querySelector("#app"));
</script>
但是它并不num
(状态)始终是初始值。
答案 0 :(得分:1)
原因是,当窗口重新渲染仅在第一个渲染中访问val
变量时,它无权在后续渲染中访问新的val
。
状态挂钩也有一个回调,用于在其中传递当前状态。
在这种情况下,使用回调读取最新状态值并确保在添加之前具有最新状态值将解决此问题。
示例:
<div id="app"></div>
<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>
<script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
<script type="text/babel">
let { useEffect, useState } = React;
let App = () => {
let [val, setVal] = useState('initial Value')
useEffect(() => {
window.addEventListener("keypress", keyPressHandler);
// document.addEventListener("keypress", keyPressHandler);
}, []);
let keyPressHandler = ({ key }) => {
console.log(val)
setVal(val => val + "-" + key); /*<- change this line*/
};
return (
<div tabindex="0" >
<h1>{val}</h1>
</div>
);
};
ReactDOM.render(<App />, document.querySelector("#app"));
</script>
已更新
关于Zohir Salak在评论中提出的问题。 我建议阅读Dan Dan Abramov撰写的此博客。 link
编辑
随着问题的更新
最好的方法是使用状态存储历史值
如果您想使用useref
,那么我能想到的就是
<div id="app"></div>
<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>
<script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
<script type="text/babel">
let { useEffect, useState, useRef, Fragment } = React;
let App = () => {
let [,ForceUpdate] = useState();
let [keyUpdate, setKeyUpdate] = useState(0);
let [num, setNum] = useState(0);
let history = useRef('null');
useEffect(() => {
window.addEventListener("keypress", keyPressHandler);
}, []);
let keyPressHandler = () => {
console.log('called keyPressHandler');
setKeyUpdate(Date.now());
};
useEffect(() => {history.current = num; ForceUpdate(Date.now()) }, [keyUpdate]);
return (
<Fragment>
<button onClick={()=>{setNum(Date.now())}}>update state</button>
<div>State : {num}</div>
<div >History: {history.current}</div>
</Fragment>
);
};
ReactDOM.render(<App />, document.querySelector("#app"));
</script>
答案 1 :(得分:0)
我认为您必须在每个更新中注册侦听器。或者,您可以将状态存储在服务或变量中,或使用useRef挂钩。
演示
<div id="app"></div>
<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>
<script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
<script type="text/babel">
let { useEffect, useState } = React;
let App = () => {
let [val, setVal] = useState('initial Value')
useEffect(() => {
window.addEventListener("keypress", keyPressHandler);
// document.addEventListener("keypress", keyPressHandler);
return () => {
window.removeEventListener("keypress", keyPressHandler);
}
});
let keyPressHandler = ({ key }) => {
console.log(val)
setVal(val + "-" + key);
};
return (
<div tabindex="0" >
<h1>{val}</h1>
</div>
);
};
ReactDOM.render(<App />, document.querySelector("#app"));
</script>