我想知道设置程序中的设置程序是否预期行为: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镜像:
更新
如果您想要something from the docs,第一句话将告诉您为什么会这样。我知道那是
setState
的意思,但是useState
Hook 也有同样的概念,但是文档很烂。 You can check out这部分文档,特别是说“第9行:” ...请记住,当X组件中的状态发生变化时,X组件及其所有子组件都将被重新渲染。我知道React会告诉您“提升状态”,这是我从未理解的事情,因为提升状态会导致大量的重新渲染。
这就是为什么按钮会重新渲染的原因。因为状态在其父级中正在更改。父级(
<App />
)的counter
状态已更改,从而触发<App />
组件及其子级(包括<Button />
)的重新呈现。在我看来,React很难控制状态和重新渲染,而Redux可以提供帮助,但是总体像
memo
,useCallback
等之类的东西对我来说都像是创可贴。如果您将状态放在错误的组件中,那将是一段糟糕的时光。包装
<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/