避免在初始化时使用 useState 挂钩到 运行
Avoid useState hook to run on initialisation
我有一个简单的按钮,用于订阅和取消订阅 onPress()
<Button
onPress={() => setRunning(running => !running)}
title={running ? 'Stop' : 'Start'}
/>
但是当我初始化状态时,它会运行致命的 useState 函数,因为我无法取消订阅我还没有订阅的内容。
const [running, setRunning] = useState(false);
useState(() => {
if (running) {
subscription.subscribe(...)
} else {
subscription.unsubscribe();
}
});
如何避免 useState 运行 除非我按下按钮?
你看到函数 get 运行 的原因是 useState
所做的,它接受初始状态,如果这是一个函数,它 运行 是获取初始状态的函数(仅在挂载时)。
您不会使用 useState
来获得订阅内容等(副作用),这根本不是它的用途。对于(副作用),通常使用 useEffect
:
useEffect(() => {
if (running) {
subscription.subscribe(/*...*/)
return () => {
subscription.unsubscribe();
};
}
}, [running]);
这样做的:
- 任何时候
running
更改,它 运行 就是您传递给它的函数。
- 如果此时
running
为真,它将订阅该事物,并且 returns 一个 cleanup function 将取消订阅它。
- 下次
running
更改时,或者当组件被卸载时,调用清理回调,执行取消订阅。
const { useState, useEffect } = React;
const subscription = {
subscribe() {
console.log("subscribed");
},
unsubscribe() {
console.log("unsubscribe");
}
};
const Example = () => {
const [running, setRunning] = useState(false);
useEffect(() => {
if (running) {
subscription.subscribe(/*...*/)
return () => {
subscription.unsubscribe();
};
}
}, [running]);
return <input type="button" value={running ? "stop" : "run"} onClick={() => setRunning(r => !r)} />;
};
const App = () => {
const [showExample, setShowExample] = useState(true);
return <div>
{showExample ? <Example /> : <em>(example hidden)</em>}
<div>
<label>
<input
type="button"
value="show/hide example"
onClick={() => setShowExample(e => !e)}
/>
</label>
</div>
</div>;
};
ReactDOM.render(<App />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>
另一个选项是在按钮单击处理程序中 subscribe/unsubscribe,但您仍然需要一个 useEffect
清理回调以确保您在卸载时取消订阅。 (这可能很棘手,因为您不能使用 running
标志来确定是否执行此操作,因为清理函数将关闭它的陈旧副本。为此您可能需要一个参考。)
我有一个简单的按钮,用于订阅和取消订阅 onPress()
<Button
onPress={() => setRunning(running => !running)}
title={running ? 'Stop' : 'Start'}
/>
但是当我初始化状态时,它会运行致命的 useState 函数,因为我无法取消订阅我还没有订阅的内容。
const [running, setRunning] = useState(false);
useState(() => {
if (running) {
subscription.subscribe(...)
} else {
subscription.unsubscribe();
}
});
如何避免 useState 运行 除非我按下按钮?
你看到函数 get 运行 的原因是 useState
所做的,它接受初始状态,如果这是一个函数,它 运行 是获取初始状态的函数(仅在挂载时)。
您不会使用 useState
来获得订阅内容等(副作用),这根本不是它的用途。对于(副作用),通常使用 useEffect
:
useEffect(() => {
if (running) {
subscription.subscribe(/*...*/)
return () => {
subscription.unsubscribe();
};
}
}, [running]);
这样做的:
- 任何时候
running
更改,它 运行 就是您传递给它的函数。 - 如果此时
running
为真,它将订阅该事物,并且 returns 一个 cleanup function 将取消订阅它。 - 下次
running
更改时,或者当组件被卸载时,调用清理回调,执行取消订阅。
const { useState, useEffect } = React;
const subscription = {
subscribe() {
console.log("subscribed");
},
unsubscribe() {
console.log("unsubscribe");
}
};
const Example = () => {
const [running, setRunning] = useState(false);
useEffect(() => {
if (running) {
subscription.subscribe(/*...*/)
return () => {
subscription.unsubscribe();
};
}
}, [running]);
return <input type="button" value={running ? "stop" : "run"} onClick={() => setRunning(r => !r)} />;
};
const App = () => {
const [showExample, setShowExample] = useState(true);
return <div>
{showExample ? <Example /> : <em>(example hidden)</em>}
<div>
<label>
<input
type="button"
value="show/hide example"
onClick={() => setShowExample(e => !e)}
/>
</label>
</div>
</div>;
};
ReactDOM.render(<App />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>
另一个选项是在按钮单击处理程序中 subscribe/unsubscribe,但您仍然需要一个 useEffect
清理回调以确保您在卸载时取消订阅。 (这可能很棘手,因为您不能使用 running
标志来确定是否执行此操作,因为清理函数将关闭它的陈旧副本。为此您可能需要一个参考。)