Cap*_*ggz 5 reactjs react-hooks
我想知道 setter in:const [state, setState] = useState(0)触发组件重新渲染是否是预期的行为。因此,如果我将 setter 作为 prop 传递给组件,它是否应该触发重新渲染,如果是,为什么?有什么办法可以避免这种情况吗?
我创建了一个非常简单的沙箱来演示行为:https : //codesandbox.io/s/bold-maxwell-qj5mi
在这里我们可以看到console.logs,每次单击都会重新渲染按钮组件,而incrementCounter传递给它的函数没有改变。是什么赋予了?
如果您记住按钮,则不会遇到此行为。
具体来说,这:
const Button = memo(({ incrementCounter }) => {
const renderCount = useRef(1);
console.log("button rendered: ", renderCount.current);
renderCount.current++;
return <button onClick={incrementCounter}>Increment</button>;
});
Run Code Online (Sandbox Code Playgroud)
代码沙盒镜像:
更新
如果你想从文档中得到一些东西,第一句话会告诉你为什么会这样。我知道那是为了setState但同样的概念遵循useState钩子,但是那种糟糕的文档。您可以查看文档的这一部分,特别是“第 9 行:”...
请记住,当 X 组件中的状态发生变化时,X 组件及其所有子组件都会重新渲染。我知道 React 会告诉你“提升状态”,这是我从未理解的,因为提升状态会导致大量的重新渲染。
这就是按钮重新呈现的原因……因为状态在其父级中发生了变化。父 ( <App />) 的counter状态已更改,这会触发<App />组件及其子组件的重新渲染,包括<Button />.
在我看来阵营是很难尽可能状态,并重新渲染控制,其终极版可以帮助,但总体喜欢的东西memo,useCallback等等。所有的感受就像创可贴给我。如果你把你的状态放在错误的组件中,你会过得很糟糕。
包装的<Button />成分在memo基本上是说:如果组件有一个父(在我们的情况下<App />),而家长重新呈现,我想看看我们所有的道具,如果我们的道具都没有从我们收到的最后一次改变,不要重新渲染。本质上,只有在我们的道具发生变化时才重新渲染。这就是为什么要memo修复这个……因为我们用来处理incrementCounterprop 的函数不会改变——它保持不变。
我在下面添加了一些示例来证明这一点。
原始答案/片段:
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);Run Code Online (Sandbox Code Playgroud)
<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>Run Code Online (Sandbox Code Playgroud)
片段 #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);Run Code Online (Sandbox Code Playgroud)
<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>Run Code Online (Sandbox Code Playgroud)
片段 #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);Run Code Online (Sandbox Code Playgroud)
<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>Run Code Online (Sandbox Code Playgroud)
片段 #4:
这个片段更像是一个思想实验,展示了如何使用渲染道具。
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);Run Code Online (Sandbox Code Playgroud)
<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>Run Code Online (Sandbox Code Playgroud)
片段 #5:
看起来这就是你想要的..这只会重新呈现 CountValue.. 这是通过将 产生的setCounter方法useState通过回调对象传递给父级来实现的。
通过这种方式,父级可以操纵状态,而不必实际持有状态。
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);Run Code Online (Sandbox Code Playgroud)
<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>Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3332 次 |
| 最近记录: |