假设有一个父组件:
function Parent({ children }) {
useEffet(() => {
// Do something when all children
// have returned their values via props (or context)
})
return <div>{ children }</div>
}
只有在所有孩子都做了什么之后我才能运行效果?我不知道孩子的确切人数,可能会有所不同:
// In this example there are 4 children
function App() {
return (
<Parent>
<Children />
<Children />
<Children />
<div>
<Children />
</div>
</Parent>
);
}
实际问题是所有孩子都可以准备一个查询(假设是一个ElasticSearch查询),父母负责实际执行整个查询。
好吧,我们可以将儿童所做的行动包装成一个承诺:
function usePromises() {
const promises = useMemo([], []);
const [result, setResult] = useState(undefined);
useEffect(() => { // this gets deferred after the initialization of all childrens, therefore all promises were created already
Promise.all(promises).then(setResult);
}, []);
function usePromise() {
return useMemo(() => {
let resolve;
promises.push(new Promise(r => resolve = r));
return resolve;
}, []);
}
return [result, usePromise];
}
现在创建一个共享管理器(在Parent内部):
const [queries, useQueryArrived] = usePromises();
// queries is either undefined or an array of queries, if all queries arrived this component will rerender
console.log(queries);
然后将useQueryArrived
传给孩子并在那里使用(在孩子们内部):
const queryArrived = useQueryArrived();
// somewhen:
queryArrived({ some: "props" });
现在的逻辑如下:
Parent第一次渲染,它将创建promises
数组。孩子们将被初始化,他们每个人都会创建一个附加到promises
的Promise,Promise的解析器会被记忆,这样每个孩子的重新渲染都会返回相同的解析器。当所有的孩子都被初始化时,React渲染并且效果被触发,这将获得所有的承诺,并在他们身上调用Promise.all
。
现在,当queryArrived
在孩子们中被召唤时,承诺会解决,有些人会在最后一个承诺解决之后,然后将所有承诺结果调用setResult
,这将导致对父亲的重新渲染,现在可以使用查询。
当所有孩子完成“做某事”时,你可以介绍一个finish
状态。
useEffect(() => {
const totalChildren = React.Children.count(children);
if (totalChildren === finish) {
console.log("All Children Done Something");
setFinished(0);
}
}, [finish]);
对于example:
function Children({ num, onClick }) {
return <button onClick={onClick}>{num}</button>;
}
function Parent({ finish, setFinished, children }) {
useEffect(() => {
const totalChildren = React.Children.count(children);
if (totalChildren === finish) {
console.log("All Children Done Something");
setFinished(0);
}
}, [finish]);
return <div>{children}</div>;
}
function App() {
const [num, setNum] = useState(0);
const [finish, setFinished] = useState(0);
const onClick = () => {
setNum(num + 1);
setFinished(finish + 1);
};
return (
<Parent finish={finish} setFinished={setFinished}>
<Children num={num} onClick={onClick} />
<Children num={num + 1} onClick={onClick} />
<Children num={num + 2} onClick={onClick} />
<div>
<Children num={num + 3} onClick={onClick} />
</div>
</Parent>
);
}
此外,您可以使用React.cloneElement
并为子组件添加“更新完成状态”功能。