这些因素的组合会导致意外的行为:
- 从父组件中的函数返回子组件
- 在子组件内有一个 onClick 监听器,通过钩子(Hook)调用更新它自己的状态
- 让同一个监听器调用更新父状态的事件监听器
我在codesandbox.io上做了一个例子:https://codesandbox.io/s/nice-kepler-bur5h
const ChildComponent = ({ onClick, title }) => {
const [isButtonClicked, setIsButtonClicked] = useState(false);
return (
<div className="childComponent">
<h2>{title}</h2>
<pre>{JSON.stringify({ isButtonClicked }, null, 2)}</pre>
<button
onClick={() => {
setIsButtonClicked(true);
onClick();
}}
>
button
</button>
</div>
);
};
function App() {
const [value, setValue] = useState(true);
const WrappedChildComponent = () => (
<ChildComponent
title="wrapped child"
onClick={() => {
setValue(!value);
}}
/>
);
return (
<div className="App">
<h2>parent</h2>
<pre>{JSON.stringify({ value }, null, 2)}</pre>
<button onClick={() => setValue(true)}>reset value</button>
<div>
<ChildComponent
title="child"
onClick={() => {
setValue(!value);
}}
/>
<WrappedChildComponent />
</div>
</div>
);
}
当单击按钮时,WrappedChildComponent
不会更新自己的 isButtonClicked
,而普通的 ChildComponent
会更新。
我希望包装的组件的行为类似于普通的子组件。
什么原因导致这种行为?
最佳答案
要实现此功能,您需要使用 useEffect
Hook 来创建 WrappedChildComponent
:
function App() {
const [value, setValue] = useState(true)
const [wrappedChildComponent, setWrappedChildComponent] = useState(null)
useEffect(() => {
setWrappedChildComponent(
<ChildComponent
title="wrapped child"
onClick={() => { setValue(!value) }
/>
)
// IMPORTANT: put `value` (since that what it depends on)
// in your dependency array so you don't
// get an infinite loop (rendering forever)
}, [value])
return (
<div>
...
<div>
<ChildComponent
title="child"
onClick={() => { setValue(!value); }}
/>
{wrappedChildComponent}
</div>
</div>
)
这修复了 WrappedComponent
内部的 isButtonClicked
无法设置其自身状态的问题。按照您的方式,WrappedComponent
本质上是在 App
内部缓存的,因此您需要创建一个额外的 Hook 才能正确处理这种情况。
此外,如果您依赖 WrappedComponent
内部 App
的 value
状态,那么您也会看到奇怪的行为(来自 App
的 value
未在 WrappedComponent
内部更新),至少是您最初的方式。上述解决方案兼顾了两者。
这是演示的链接:
关于reactjs - 将组件包装在函数、事件处理和状态 Hook 内的组合表现出意外,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57443756/