我有一个非常重的(计算上(功能组件(Parent(,它没有状态,并且很少有局部状态的子组件。子级仅依赖于从父级发送的道具。
我将一个函数传递给其中一个子项(ChildA(,以更改Parent上变量的值。
该变量是另一个子组件(ChildB(的道具之一,该组件具有基于该道具的状态,并在useEffect钩子中更新该状态。
当作为道具传递的值在父组件上发生更改时,ChildB组件不会重新渲染。
当然,在Parent上引入state(useState钩子(可以修复这一问题,但会一遍又一遍地重新渲染父级,并降低性能,因为Parent有500多个嵌套组件,所有组件都会重新渲染。
引入某种商店(Redux、MobX(可能会解决这个问题,但这太过分了。
一个简化的例子:
import React, { useEffect, useState } from "react";
export default function App() {
return <Parent />
}
const ChildA = ({ onAction }) => {
return <button onClick={onAction}>CLICK</button>;
};
const ChildB = ({ coreValue }) => {
const [value, setValue] = useState(0);
useEffect(() => {
setValue(coreValue);
}, [coreValue]);
return <div>My value: {value}</div>;
};
const Parent = () => {
let calculatedValue = 0;
const changeValue = () => {
calculatedValue += Math.random();
};
return (
<div>
<ChildA onAction={changeValue} />
<ChildB coreValue={calculatedValue} />
</div>
);
};
您可以在此处测试代码:https://codesandbox.io/s/vigilant-wave-r27rg
如何在道具更改时仅重新渲染ChildB?
您必须将值存储在父组件状态中,并将父组件状态值发送到ChildB,在那里您不需要维护状态并使用Effect挂钩来捕捉更改。请参阅此处的代码:https://codesandbox.io/s/adoring-chatelet-sjjfs
import React, { useState } from "react";
export default function App() {
return <Parent />;
}
const ChildA = ({ onAction }) => {
return <button onClick={onAction}>CLICK</button>;
};
const ChildB = ({ coreValue }) => {
return <div>My value: {coreValue}</div>;
};
const Parent = () => {
const [value, setValue] = useState(0);
const changeValue = () => {
setValue(value + Math.random());
};
return (
<div>
<ChildA onAction={changeValue} />
<ChildB coreValue={value} />
</div>
);
};
useCallback
和memo
防止了不必要的重新渲染。请注意,无论Parent
或ChildB
状态更改的次数如何,ChildA都不会重新渲染。此外,您当前的示例在ChildB
中不需要useState
/useEffect
https://codesandbox.io/s/usecallback-and-memo-ptkuj
import React, { useEffect, useState, memo, useCallback } from "react";
export default function App() {
return <Parent />;
}
const ChildA = memo(({ onAction }) => {
console.log("ChildA rendering");
return <button onClick={onAction}>CLICK</button>;
});
const ChildB = memo(({ coreValue }) => {
const [value, setValue] = useState(0);
useEffect(() => {
setValue(coreValue);
}, [coreValue]);
return <div>My value: {value}</div>;
});
const Parent = () => {
const [calculatedValue, setCalculatedValue] = useState(0);
const changeValue = useCallback(() => {
setCalculatedValue(c => (c += Math.random()));
}, []);
return (
<div>
<ChildA onAction={changeValue} />
<ChildB coreValue={calculatedValue} />
</div>
);
};