gpt4 book ai didi

javascript - 如何在不重新触发 useEffect 的情况下访问 useEffect 中的状态?

转载 作者:行者123 更新时间:2023-12-05 00:25:38 26 4
gpt4 key购买 nike

我需要添加一些与 React 之外的对象交互的事件处理程序(以 Google map 为例)。
在这个处理函数内部,我想访问一些可以发送给这个外部对象的状态。
如果我将状态作为依赖项传递给效果,它可以工作(我可以正确访问状态)但是 每次状态更改时都会添加添加/删除处理程序。
如果我不将状态作为依赖项传递,则添加/删除处理程序会添加适当的次数(基本上一次),但状态永远不会更新(或更准确地说,处理程序无法提取最新状态) .
代码笔示例:
也许最好用 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"));

概括
如果效果的 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"));

注意添加/删除处理程序如何只添加一次(显然忽略了 App 组件未卸载的事实),尽管状态发生了变化。
我正在寻找一种用钩子(Hook)复制它的方法

最佳答案

您可以使用mutable refs将读取当前状态与效果依赖项解耦:

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

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"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js" integrity="sha256-32Gmw5rBDXyMjg/73FgpukoTZdMrxuYW7tj8adbN8z4=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js" integrity="sha256-bjQ42ac3EN0GqK40pC9gGi/YixvKyZ24qMP/9HiGW7w=" crossorigin="anonymous"></script>
<div id="root"></div>
<div id="outside-react">div</div>
<p>Update counter with + button, then mouseover the div to see recent counter state.</p>

事件监听器只会在挂载/卸载时添加/删除一次。当前状态 n可以在 useEffect 里面阅读没有将其设置为依赖项( [] deps),因此不会重新触发更改。
你可以想到 useRef作为函数组件和 Hooks 的可变实例变量。类组件中的等价物是 this上下文 - 这就是为什么 this.state.nhandleMouseOver类组件示例的始终返回最新状态并正常工作。
有一个很棒的 example by Dan AbramovsetInterval 展示上述模式.该博客文章还说明了 useCallback 的潜在问题。以及当每次状态更改都读取/删除事件监听器时。
其他有用的示例是(全局)事件处理程序,如 os.addEventListener或与 React 边缘的外部库/框架集成。
注意:React 文档推荐给 use this pattern sparingly .从我的 Angular 来看,在您只需要“最新状态”的情况下,它是一个可行的替代方案——独立于 React 渲染周期更新。通过使用可变变量,我们打破了具有潜在陈旧闭包值的函数闭包范围。
独立于依赖项编写状态还有其他选择 - 您可以查看 How to register event with useEffect hooks?了解更多信息。

关于javascript - 如何在不重新触发 useEffect 的情况下访问 useEffect 中的状态?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63224151/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com