[英]Update a specific variable of a useState array (by its place) in React.js?
[英]useState place in lifecycle in react
我真的很想了解反应功能组件的生命周期。 在许多网站中,您会看到以下三个步骤:
1-安装 2-渲染 3-卸载。
但是,在 useeffect() function 之前编写的其他代码又如何假设:
const Countdown = () => {
let x = 0;
const [countDown, setCountDown] = useState(10)
x = x + 1
if(x > 100) {
x = 0
}
useEffect(() => {
const interval = setInterval(() => {
setCountDown(countDown - 1);
console.log(x)
}, 1000);
}, [countDown]);
};
我想知道:
当 countDown state 和 x 变量在 useEffect 之前或之后(或在其内部)声明时?
当声明if
或for
phrase 时(在此示例中为if
phrase),它们确实在 useEffect 中吗?
什么是加载页面顺序? 执行的起点是什么?
1-安装 2-渲染 4-卸载。
它更像是(文档):
useLayoutEffect
调度的相关布局效果回调)useEffect
安排的相关非布局效果回调))useLayoutEffect
调度的相关布局效果回调)useEffect
安排的相关非布局效果回调))当 countDown state 和 x 变量在 useEffect 之前或之后(或在其内部)声明时?
前。 React 库无法更改 JavaScript 代码执行的方式。 useState
调用和关联的声明在useEffect
调用之前,因此它们发生在它之前。
当声明 if 或 for phrase 时(在此示例中为 if phrase),它们确实在 useEffect 中吗?
不,只有useEffect
回调中的代码被调用为效果。
这里的循环是:
x
变量并将其设置为0
。countDown
和setCountDown
并调用useState
,它在实例存储中分配一个 state 槽; 您的代码将useState
返回的内容(初始 state 值和设置器)存储在这些常量中。x = x + 1
语句运行,将x
更新为1
。if
语句运行,但条件永远不会为真 — x
是局部变量,而不是 state 成员,因此此时它的值将始终为1
。useEffect
的调用会在countDown
更改时安排效果回调。Countdown
返回元素。Countdown
应返回的元素并将它们提交给 DOM(使 DOM 显示它们所描述的内容)。useEffect
回调( useEffect
回调总是在挂载之后调用)
setCountDown
。x
,它将是1
。setCountDown
来更改值。x
变量并将其设置为0
。countDown
和setCountDown
并调用useState
,它从实例存储中检索更新后的 state; 您的代码将useState
返回的内容(当前 state 值和设置器)存储在这些常量中。x = x + 1
语句运行,将x
更新为1
。if
语句运行,但条件永远不会为真。useEffect
的调用会在countDown
更改时安排效果回调。Countdown
返回元素。countDown
改变了,React 调用你的useEffect
回调
setCountDown
。x
,它将是1
。您显示的代码中有几个错误
countDown
更改时都会创建一个新计时器。 这将很快导致成百上千个计时器全部触发更新调用。 你应该:
countDown
作为依赖项,因此效果仅在挂载时运行。 然后使用setCountDown
的回调形式。x
的值,但它是一个局部变量,因此每次都会重新创建。countDown
达到0
时没有什么特别的事情发生,所以它会一直持续到-1
、 -2
等。这是带有一些注释的更新版本。 我打算删除x
因为它并没有真正用于任何用途,但后来认为最好留下评论。 我没有对上面的#4 做任何事情,因为我不确定你想做什么。
const Countdown = () => {
let x = 0;
const [countDown, setCountDown] = useState(10);
x = x + 1;
if (x > 100) { // `x` will always be `1` here, remember that
x = 0; // `x` is a *local variable*
}
useEffect(() => {
const interval = setInterval(() => {
// Use the callback form of the setter so you can update the
// up-to-date value
setCountDown((c) => c - 1);
// Will always show 1
console.log(x);
}, 1000);
// Return a cleanup callback that removes the interval timer
return () => {
clearInterval(interval);
};
}, []);
// ^^ don't use `countDown` as a dependency (in this particular case),
// since we don't use it (anymore, now we use the callback setter)
// Return some elements
return <div>{countDown}</div>;
};
const { useState, useEffect } = React; const Countdown = () => { let x = 0; const [countDown, setCountDown] = useState(10); x = x + 1; if (x > 100) { // `x` will always be `1` here, remember that x = 0; // `x` is a *local variable* } useEffect(() => { const interval = setInterval(() => { // Use the callback form of the setter so you can update the // up-to-date value setCountDown((c) => c - 1); // Will always show 1 console.log(x); }, 1000); // Return a cleanup callback that removes the interval timer return () => { clearInterval(interval); }; }, []); // ^^ don't use `countDown` as a dependency (in this particular case), // since we don't use it (anymore, now we use the callback setter) // Return some elements return <div>{countDown}</div>; }; const Example = () => { return <Countdown />; }; const root = ReactDOM.createRoot(document.getElementById("root")); root.render(<Example />);
<div id="root"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>
如果我们希望倒计时在达到0
时停止并关闭计时器,我们可以采用多种方法,请参阅两个实例中显示不同方法的注释:
const { useState, useEffect } = React; const Countdown = () => { const [countDown, setCountDown] = useState(10); useEffect(() => { const interval = setInterval(() => { // We could cancel the interval from within the setter // callback. It's a bit dodgy, though, to have side- // effects in setter callbacks. setCountDown((c) => { const updated = c - 1; if (updated === 0) { clearInterval(interval); } return updated; }); }, 1000); return () => { clearInterval(interval); }; }, []); return <div>{countDown === 0? "Done": countDown}</div>; }; const Example = () => { return <Countdown />; }; const root = ReactDOM.createRoot(document.getElementById("root")); root.render(<Example />);
<div id="root"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>
const { useState, useEffect, useRef } = React; const Countdown = () => { // We could store the timer handle in a ref (which is maintained // across renders) and use a second `useEffect` to cancel it when // `countDown` reaches zero. const intervalRef = useRef(0); const [countDown, setCountDown] = useState(10); useEffect(() => { intervalRef.current = setInterval(() => { setCountDown((c) => c - 1); }, 1000); return () => { // (It's okay if this tries to clear an interval that // isn't running anymore.) clearInterval(intervalRef.current); }; }, []); useEffect(() => { if (countDown === 0) { clearInterval(intervalRef.current); } }, [countDown]); return <div>{countDown === 0? "Done": countDown}</div>; }; const Example = () => { return <Countdown />; }; const root = ReactDOM.createRoot(document.getElementById("root")); root.render(<Example />);
<div id="root"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>
useEffec 在生命周期中的位置
在组件代码中放置useEffect
的位置并不重要,所有效果将始终在每次渲染后运行。 useEffect
在组件代码中的唯一位置可能是重要的是如果您有多个useEffect
s,因为它们将按照您编写它们的顺序执行。
x 变量/状态
至于你的x
变量,这不是在反应中起作用的东西,因为该变量将在每次渲染时重新声明。 跟踪 state 的方法是使用useState
。 我认为您可以从同一个 state 跟踪这两个值,但是如果您想要额外的显式 state,则需要使用另一个useState
挂钩。 一般约定是在组件的顶部声明 state 钩子(但它们不是必须的)。
如果语句
React hooks 的规则之一是所有 hooks 必须在每次渲染时运行。 您仍然可以在组件和自定义挂钩中使用 id 语句,但它们必须在组件中的所有挂钩之后运行,或者您可以在挂钩中声明 then - 这样挂钩会在每次渲染时执行,但其中的代码块将仅在 if 语句的条件为真时运行。
useEffect(() => {
if (condition) {
code to run...
}
}, [countDown]);
补充笔记
关于useEffect
需要记住的另一件重要事情是,有时它们需要用 return 语句清除。 在您的情况下,您声明了一个间隔,而没有返回clearInterval
,您的useEffect
将在每个渲染上创建一个额外的间隔,这将导致您的计数器不同步
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.