我有一些深度嵌套的react组件。让我们将它们限制为3个嵌套级别,并将它们称为“父母”,“子代”和“孙子代”。整个应用程序的状态存储在Parent组件的单个对象中,并使用useState
挂钩进行管理。
export interface Parent {
children: Child[];
}
export interface Child {
weight: number;
grandchildren: Grandchild[];
}
export interface Grandchild {
age: number;
}
我有两种替代的方式来更新状态:通过在每个级别上增量更新状态并冒泡到根,或通过增量构建包含目标节点路径的数组并在根级别上只更新一次状态
方法1:增量更新
export interface ContainerProps<T> {
index: number;
value: T;
updateHandler: any;
}
const ParentContainer: React.FC = () => {
const [value, setValue] = useState<Parent>(); // source of truth
const updateChild = (childIndex: number, child: Child) => {
value.children[childIndex] = child;
setValue({...value});
};
return (
<div className="Parent">
{value.children.map((child, index) => (
<ChildContainer key={index} index={index} value={child} updateHandler={updateChild} />
))}
</div>
);
}
const ChildContainer: React.FC<ContainerProps<Child>> = ({index, value, updateHandler}) => {
const updateGrandchild = (grandchildIndex: number, grandchild: Grandchild) => {
value.grandchildren[grandchildIndex] = grandchild;
updateHandler(value);
};
const increaseWeight = () => {
updateHandler(index, {
weight: value.weight + 1
})
}
return (
<div className="Child">
<button onClick={event => increaseWeight()}>
Increase weight
</button>
{value.grandchildren.map((grandchild, index) => (
<ChildContainer key={index} index={index} value={grandchild} updateHandler={updateGrandchild} />
))}
</div>
);
}
const GrandchildContainer: React.FC<ContainerProps<Grandchild>> = ({index, value, updateHandler}) => {
const increaseAge = () => {
updateHandler(index, {
age: value.age + 1
})
}
return (
<div className="Grandchild">
<button onClick={event => increaseAge()}>
Increase age
</button>
</div>
);
}
方法2:路径数组
export type PathElement = string | number;
const ParentContainer: React.FC = () => {
const [value, setValue] = useState<Parent>(); // source of truth
// e.g. to set a grandchild's age to 2,
// path = [0, 'grandchildren', 2, 'age'], item = 2
const update = (path: PathElement[], item: any) => {
let node = value['children'];
for (let i = 0; i < path.length; i++) {
if (i === path.length - 1) {
node[path[i]] = item;
}
else {
node = node[path[i]];
}
}
setValue({...value});
};
return (
<div className="Parent">
{value.children.map((child, index) => (
<ChildContainer key={index} index={index} value={child} updateHandler={updateChild} />
))}
</div>
);
}
const ChildContainer: React.FC<ContainerProps<Child>> = ({index, value, updateHandler}) => {
const updateGrandchild = (path: PathElement, item: any) => {
updateHandler([index, 'grandchildren'].concat(path), item);
};
const increaseWeight = () => {
updateHandler([index, 'weight'], value.weight + 1);
}
return (
<div className="Child">
<button onClick={event => increaseWeight()}>
Increase weight
</button>
{value.grandchildren.map((grandchild, index) => (
<ChildContainer key={index} index={index} value={grandchild} updateHandler={updateGrandchild} />
))}
</div>
);
}
const GrandchildContainer: React.FC<ContainerProps<Grandchild>> = ({index, value, updateHandler}) => {
const increaseAge = () => {
updateHandler([index, 'age'], value.age + 1);
}
return (
<div className="Grandchild">
<button onClick={event => increaseAge()}>
Increase age
</button>
</div>
);
}
我的想法:
我知道,对于前端代码,可维护性优先于较小的性能问题。但是,在一种用例中,方法2更适合:假设父组件具有变量totalAge
,该变量在孙子的年龄更新时都会更新。使用方法1,如果不传递额外的年龄处理程序或向现有的更新处理程序添加额外的参数,就不可能知道哪个元素已更新。使用方法2,您可以简单地检查路径的最后一个元素是否为“年龄”。
有更好的方法吗?