gpt4 book ai didi

reactjs - react useState setter导致重新渲染

转载 作者:行者123 更新时间:2023-12-03 16:36:45 27 4
gpt4 key购买 nike

我想知道设置程序中的设置程序是否预期行为:const [state, setState] = useState(0)触发组件重新渲染。因此,如果我将setter作为 Prop 传递给组件,是否应该触发重新渲染?如果是,为什么?有什么办法可以避免这种情况?

我创建了一个非常简单的沙箱来演示此行为:https://codesandbox.io/s/bold-maxwell-qj5mi

在这里我们可以看到,当查看console.logs时,每次单击都会重新渲染按钮组件,而传递给它的incrementCounter函数没有改变。是什么赋予了?

最佳答案

如果您按一下memoize按钮,则不会遇到此现象。

具体来说,这是:

const Button = memo(({ incrementCounter }) => {
const renderCount = useRef(1);
console.log("button rendered: ", renderCount.current);
renderCount.current++;
return <button onClick={incrementCounter}>Increment</button>;
});

CodeSandbox镜像:

Edit delicate-http-b6sym

更新

如果您想要 something from the docs,第一句话将告诉您为什么会这样。我知道那是 setState的意思,但是 useState Hook 也有同样的概念,但是文档很烂。 You can check out这部分文档,特别是说“第9行:” ...

请记住,当X组件中的状态发生变化时,X组件及其所有子组件都将被重新渲染。我知道React会告诉您“提升状态”,这是我从未理解的事情,因为提升状态会导致大量的重新渲染。

这就是为什么按钮会重新渲染的原因。因为状态在其父级中正在更改。父级( <App />)的 counter状态已更改,从而触发 <App />组件及其子级(包括 <Button />)的重新呈现。

在我看来,React很难控制状态和重新渲染,而Redux可以提供帮助,但是总体像 memouseCallback等之类的东西对我来说都像是创可贴。如果您将状态放在错误的组件中,那将是一段糟糕的时光。

包装 <Button />组件 in memo 基本上是这样说的:如果该组件有一个父级(在我们的情况下为 <App />),并且该父级重新渲染,则我要查看我们所有的 Prop ,并且如果我们的 Prop 与我们收到的相比没有变化上一次,请勿重新渲染。本质上,只有在我们的 Prop 改变的情况下才重新渲染。这就是 memo修复此问题的原因。.因为我们用来处理 incrementCounter Prop 的函数没有改变-它保持不变。

我在下面添加了一些示例来说明这一点。

原始答案/摘录:

const { memo, useState, useCallback, useEffect, useRef } = React;
const { render } = ReactDOM;

const App = () => {
const [counter, setCounter] = useState(0);
const incrementCounter = useCallback(() => {
setCounter(c => c + 1);
}, [setCounter]);

useEffect(() => {
console.log("increment changed!");
}, [incrementCounter]);

return (
<div>
<CountValue counter={counter} />
<Button incrementCounter={incrementCounter} />
</div>
);
}

const CountValue = ({ counter }) => {
return <div>Count value: {counter}</div>;
};

const Button = memo(({ incrementCounter }) => {
const renderCount = useRef(1);
console.log("button rendered: ", renderCount.current);
renderCount.current++;
return <button onClick={incrementCounter}>Increment</button>
});

render(<App />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.12.0/umd/react-dom.production.min.js"></script>



SNIPPET#2:

此代码段显示了如何重新渲染所有内容,而不仅仅是按钮。

const { useState, useEffect } = React;
const { render } = ReactDOM;

const App = () => {
console.log("App rendered");
const [counter, setCounter] = useState(0);
const incrementCounter = () => setCounter(c => c + 1);

useEffect(() => {
console.log(" - Increment fired!");
console.log();
}, [incrementCounter]);

return (
<div>
<CountValue counter={counter} />
<Button incrementCounter={incrementCounter} />
<p>Open console</p>
</div>
);
}

const CountValue = ({ counter }) => {
console.log("CountValue rendered");
return <div>Count value: {counter}</div>;
};

const Button = ({ incrementCounter }) => {
console.log("Button rendered");
return <button onClick={incrementCounter}>Increment</button>
};

render(<App />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.12.0/umd/react-dom.production.min.js"></script>



SNIPPET#3:

此代码段显示了如何将状态等移动到 <CountValue />组件中,但 <App />组件不会重新呈现。

const { useState, useEffect } = React;
const { render } = ReactDOM;

const App = () => {
console.log("App rendered");

return (
<div>
<CountValue />
<p>Open console</p>
</div>
);
}

const CountValue = () => {
console.log("CountValue rendered");

const [counter, setCounter] = useState(0);
const incrementCounter = () => setCounter(c => c + 1);

return (
<div>
<div>Count value: {counter}</div>
<Button incrementCounter={incrementCounter} />
</div>
);
};

const Button = ({ incrementCounter }) => {
console.log("Button rendered");
console.log();
return <button onClick={incrementCounter}>Increment</button>
};

render(<App />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.12.0/umd/react-dom.production.min.js"></script>



SNIPPET#4:

该片段更多是一个思想实验,展示了如何使用渲染 Prop 。

const { useState, useEffect } = React;
const { render } = ReactDOM;

const App = () => {
console.log("App rendered");

return (
<div>
<CountValue present={({ increment, counter }) => {
return (
<div><Button incrementCounter={() => increment()} />
<p>Counter Value: {counter}</p></div>
)
}} />
<p>Open console</p>
</div>
);
}

const CountValue = ({ present }) => {
const [counter, setCounter] = useState(0);

const increment = () => {
setCounter(c => c + 1);
}

console.log("CountValue rendered");
return (
<React.Fragment>
{present({ increment, counter })}
</React.Fragment>
);
};

const Button = ({ incrementCounter }) => {
console.log("Button rendered");
return <button onClick={incrementCounter}>Increment</button>
};

render(<App />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.12.0/umd/react-dom.production.min.js"></script>



SNIPPET#5:

看来这就是您要执行的操作。这仅重新渲染CountValue。这是通过将 setCounter生成的 useState方法通过回调对象传递给父级来实现的。

这样, parent 就可以操纵状态,而不必实际保持状态。

const { useState, useEffect } = React;
const { render } = ReactDOM;

const App = () => {
console.log("App rendered");
let increaseCount;

return (
<div>
<CountValue callback={({increment}) => increaseCount = increment} />
<Button incrementCounter={() => increaseCount()} />
<p>Open console</p>
</div>
);
}

const CountValue = ({ callback }) => {
console.log("CountValue rendered");
const [counter, setCounter] = useState(0);

callback && callback({
increment: () => setCounter(c => c + 1)
});

return <p>Counter Value: {counter}</p>;
};

const Button = ({ incrementCounter }) => {
console.log("Button rendered");
return <button onClick={incrementCounter}>Increment</button>
};

render(<App />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.12.0/umd/react-dom.production.min.js"></script>

关于reactjs - react useState setter导致重新渲染,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60386389/

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