我有一个父组件,其中有几个子组件。我希望能够根据孩子的状态设置父母的状态。
例如,假设一个有3个孩子的父组件都从服务器获得了他们的状态。然后,他们设置父母的身份,以便父母可以查看所有孩子是否有问题,或者只有几个。
父母:
const Parent = () => {
const [hasIssues, setHasIssues] = useState({
"child-item-1": false,
"child-item-2": false,
"child-item-3": false
});
const issuesHandler = (childName, childStatus) => {
setHasIssues({ ...hasIssues, [childName]: childStatus });
};
return (
<div>
<pre>{JSON.stringify(hasIssues, null, 2)}</pre>
<div>
<ChildItemOne issuesHandler={issuesHandler} />
<ChildItemTwo issuesHandler={issuesHandler} />
<ChildItemThree issuesHandler={issuesHandler} />
</div>
</div>
);
};
一个孩子的例子:
const ChildItemOne = ({ issuesHandler }) => {
// Imagine this is actually retrieved from a server
const hasIssues = Math.random() <= 0.75;
issuesHandler("child-item-1", hasIssues);
return <div>{`child-item-1: ${hasIssues}`}</div>;
};
当然,此示例将超过最大更新深度。我尝试通过使用以下示例来防止这种情况:
useEffect(() => {
issuesHandler("child-item-1", hasIssues);
}, [hasIssues, issuesHandler]);
但是这仍然没有预期的结果,因为状态仍在不断更新,但不会超过最大更新深度:Codesandbox example。
我也尝试过使用useCallback
,它没有任何作用。将useEffect
的依赖项更改为一个空数组(ESLint仍然不允许我在本地执行此操作),也无效:
从子组件设置父状态而不引起持续的重新渲染的最佳方法是什么?
答案 0 :(得分:1)
这看起来像是useContext的情况。 React Context用于提供对父组件和子组件的状态/功能的访问,而无需使用回调地狱。
首先在应用程序中的某个位置创建一个虚拟上下文组件,然后将其导入到Parent组件,并使用它包装需要传递的组件。然后,您无需将状态/功能传递给孩子,只需将其传递给上下文组件即可。
在子组件中,您可以使用useContext访问这些数据。
issueContext.js(虚拟上下文文件)
// app/context/issueContext.js
import { createContext } from 'react';
export default createContext();
父组件
import IssueContext from 'issueContext';
const Parent = () => {
const [hasIssues, setHasIssues] = useState({ ... });
const issuesHandler = (childName, childStatus) => { ... };
return (
<>
<IssueContext.Provider value={{ hasIssues, setHasIssues, issuesHandler }} >
<ChildItemOne />
<ChildItemTwo />
<ChildItemThree />
</IssueContext>
</>
);
};
子组件
import React, {useContext} from 'react';
import IssueContext from 'issueContext';
const ChildItemOne = () => {
const {hasIssues, setHasIssues, issuesHandler} = useContext(IssueContext);
if (something wrong) {
sethasIssues();
issuesHandler("child-item-1", hasIssues);
}
};
答案 1 :(得分:1)
将const hasIssues = Math.random() <= 0.75;
移至useEffect()
回调中。这将防止重新渲染循环,因为该值只会生成一次。
这也将更好地模拟对服务器的调用,因为在安装组件时它只会发生一次。
const ChildItemOne = ({ issuesHandler, hasIssues }) => {
useEffect(() => {
// Imagine this is actually retrieved from a server
issuesHandler("child-item-1", Math.random() <= 0.75);
}, [issuesHandler]);
return <div>{`child-item-1: ${hasIssues}`}</div>;
};
父级也应将hasIssues
返回给带有issuesHandler
的子套useCallback()
,并在setHasIssues()
中使用更新程序回调。这样可以防止在每个渲染器上重新创建issuesHandler
,进而导致调用子级useEffect()
,依此类推...
const Parent = () => {
const [hasIssues, setHasIssues] = useState({
"child-item-1": false,
"child-item-2": false,
"child-item-3": false
});
const issuesHandler = useCallback((childName, childStatus) => {
setHasIssues(state => ({ ...state, [childName]: childStatus }))
}, [setHasIssues]);
return (
<div>
<pre>{JSON.stringify(hasIssues, null, 2)}</pre>
<div>
<ChildItemOne issuesHandler={issuesHandler} hasIssues={hasIssues['child-item-1']} />
<ChildItemTwo issuesHandler={issuesHandler} hasIssues={hasIssues['child-item-2']} />
<ChildItemThree issuesHandler={issuesHandler} hasIssues={hasIssues['child-item-3']} />
</div>
</div>
);
};
实时示例:
const { useState, useCallback, useEffect } = React;
const ChildItemOne = ({ issuesHandler, hasIssues }) => {
useEffect(() => {
// Imagine this is actually retrieved from a server
issuesHandler("child-item-1", Math.random() <= 0.75);
}, [issuesHandler]);
return <div>{`child-item-1: ${hasIssues}`}</div>;
};
const Parent = () => {
const [hasIssues, setHasIssues] = useState({
"child-item-1": false,
"child-item-2": false,
"child-item-3": false
});
const issuesHandler = useCallback((childName, childStatus) => {
setHasIssues(state => ({ ...state, [childName]: childStatus }))
}, [setHasIssues]);
return (
<div>
<pre>{JSON.stringify(hasIssues, null, 2)}</pre>
<div>
<ChildItemOne issuesHandler={issuesHandler} hasIssues={hasIssues['child-item-1']} />
</div>
</div>
);
};
ReactDOM.render(
<Parent />,
root
);
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>