Chr*_*ris 17 javascript reactjs react-hooks
我需要添加一些与 React 外部的对象交互的事件处理程序(以 Google 地图为例)。在这个处理函数中,我想访问一些可以发送到这个外部对象的状态。
如果我将状态作为效果的依赖项传递,它会起作用(我可以正确访问状态),但每次状态更改时都会添加添加/删除处理程序。
如果我不将状态作为依赖项传递,则添加/删除处理程序会添加适当的次数(基本上一次),但状态永远不会更新(或更准确地说,处理程序无法提取最新状态) 。
也许最好用 Codepen 来解释: https ://codepen.io/cjke/pen/dyMbMYr?editors=0010
const App = () => {
const [n, setN] = React.useState(0);
React.useEffect(() => {
const os = document.getElementById('outside-react')
const handleMouseOver = () => {
// I know innerHTML isn't "react" - this is an example of interacting with an element outside of React
os.innerHTML = `N=${n}`
}
console.log('Add handler')
os.addEventListener('mouseover', handleMouseOver)
return () => {
console.log('Remove handler')
os.removeEventListener('mouseover', handleMouseOver)
}
}, []) // <-- I can change this to [n] and `n` can be accessed, but add/remove keeps getting invoked
return (
<div>
<button onClick={() => setN(n + 1)}>+</button>
</div>
);
};
ReactDOM.render(<App />, document.getElementById("root"));
Run Code Online (Sandbox Code Playgroud)
如果效果的 dep 列表是[n]更新状态,但会为每个状态更改添加/删除添加/删除处理程序。如果效果的 dep 列表是[]添加/删除处理程序,则可以正常工作,但状态始终为 0(初始状态)。
我想要两者的混合。访问状态,但只能访问一次 useEffect(就好像依赖项是[])。
我知道如何使用生命周期方法解决它,但不确定它如何与 Hooks 一起使用。
如果上面是一个类组件,它看起来像:
class App extends React.Component {
constructor(props) {
super(props)
this.state = { n: 0 };
}
handleMouseOver = () => {
const os = document.getElementById("outside-react");
os.innerHTML = `N=${this.state.n}`;
};
componentDidMount() {
console.log("Add handler");
const os = document.getElementById("outside-react");
os.addEventListener("mouseover", this.handleMouseOver);
}
componentWillUnmount() {
console.log("Remove handler");
const os = document.getElementById("outside-react");
os.removeEventListener("mouseover", handleMouseOver);
}
render() {
const { n } = this.state;
return (
<div>
<strong>Info:</strong> Click button to update N in state, then hover the
orange box. Open the console to see how frequently the handler is
added/removed
<br />
<button onClick={() => this.setState({ n: n + 1 })}>+</button>
<br />
state inside react: {n}
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
Run Code Online (Sandbox Code Playgroud)
请注意添加/删除处理程序如何仅添加一次(显然忽略了应用程序组件未卸载的事实),尽管状态发生了变化。
我正在寻找一种用钩子复制它的方法
for*_*d04 21
您可以使用可变引用将读取当前状态与效果依赖关系解耦:
const [n, setN] = useState(0);
const nRef = useRef(n); // define mutable ref
useEffect(() => { nRef.current = n }) // nRef is updated after each render
useEffect(() => {
const handleMouseOver = () => {
os.innerHTML = `N=${nRef.current}` // n always has latest state here
}
os.addEventListener('mouseover', handleMouseOver)
return () => { os.removeEventListener('mouseover', handleMouseOver) }
}, []) // no need to set dependencies
Run Code Online (Sandbox Code Playgroud)
const [n, setN] = useState(0);
const nRef = useRef(n); // define mutable ref
useEffect(() => { nRef.current = n }) // nRef is updated after each render
useEffect(() => {
const handleMouseOver = () => {
os.innerHTML = `N=${nRef.current}` // n always has latest state here
}
os.addEventListener('mouseover', handleMouseOver)
return () => { os.removeEventListener('mouseover', handleMouseOver) }
}, []) // no need to set dependencies
Run Code Online (Sandbox Code Playgroud)
const App = () => {
const [n, setN] = React.useState(0);
const nRef = React.useRef(n); // define mutable ref
React.useEffect(() => { nRef.current = n }) // nRef.current is updated after each render
React.useEffect(() => {
const os = document.getElementById('outside-react')
const handleMouseOver = () => {
os.innerHTML = `N=${nRef.current}` // n always has latest state here
}
os.addEventListener('mouseover', handleMouseOver)
return () => { os.removeEventListener('mouseover', handleMouseOver) }
}, []) // no need to set dependencies
return (
<div>
<button onClick={() => setN(prev => prev + 1)}>+</button>
</div>
);
};
ReactDOM.render(<App />, document.getElementById("root"));Run Code Online (Sandbox Code Playgroud)
事件监听器只会在安装/卸载时添加/删除一次。n可以在内部读取当前状态useEffect,而无需将其设置为依赖项([]deps ),因此不会重新触发更改。
您可以将其视为useRef函数组件和 Hook 的可变实例变量。类组件中的等效项是this上下文 - 这就是为什么类组件示例this.state.n中总是返回最新状态并起作用的原因。handleMouseOver
Dan Abramov有一个很好的例子,展示了上面的模式setInterval。该博客文章还说明了useCallback每次状态更改时读取/删除事件侦听器的潜在问题。
其他有用的示例是(全局)事件处理程序,例如os.addEventListenerReact 边缘的外部库/框架或与外部库/框架的集成。
注意: React 文档建议谨慎使用此模式。从我的角度来看,在您只需要“最新状态”的情况下,这是一个可行的替代方案 - 独立于 React 渲染周期更新。通过使用可变变量,我们可以使用可能过时的闭包值来突破函数闭包范围。
独立于依赖项编写状态还有其他选择 - 您可以查看如何使用 useEffect hooks 注册事件?了解更多信息。
| 归档时间: |
|
| 查看次数: |
7373 次 |
| 最近记录: |