我在SO上讨论了几个关于功能组件默认道具的问题,他们都建议使用ES6默认参数。以下是这些问题的链接。
- React - 解构时的默认道具与 ES6 默认参数(性能问题(
- 反应功能组件默认道具与默认参数
但是,当我使用该方法编写在道具更改上运行效果的组件时,我会遇到非基元的不需要的行为。例如,以下代码将导致无限循环。
const Parent = () => {
let somethingUndefined;
return (
<div>
<Child prop={somethingUndefined} />
</div>
);
};
const Child = ({ prop = {a: 1} }) => {
const [x, setX] = React.useState(1);
React.useEffect(() => {
setX(x + 1);
}, [prop]);
return <div>{x}, {prop.a}</div>;
};
ReactDOM.render(<Parent />, document.getElementsByTagName('body')[0]);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.11.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script>
我尝试了两种方法来试图规避这个问题。首先,只需分配一个包含默认值的不同变量,并将未修改的 prop 放入依赖项数组中。即
const Child = ({ prop }) => {
const [x, setX] = React.useState(1);
const defaultedProp = prop || {a: 1};
React.useEffect(() => {
setX(x + 1);
}, [prop]);
// Note we use prop and not defaultedProp here to avoid runnning into the issue above.
return <div>{x}, {defaultedProp.a}</div>;
};
另一种方法是只使用类似(prop || {a:1})
的东西来代替prop
在你使用它的任何地方,除了在依赖数组中。
const Child = ({ prop }) => {
const [x, setX] = React.useState(1);
React.useEffect(() => {
setX(x + 1);
}, [prop]);
return <div>{x}, {(prop || {a: 1}).a}</div>;
};
但是这两种解决方案似乎都不是最佳的,因为它需要大量浪费的精力(和笨重的代码(。
defaultProps
也是无限循环问题的解决方案,但它已被弃用。请注意,此 rfc 中提供的示例还在代码中使用 ES6 默认参数。
我错过了什么吗?有没有更好的方法可以在对道具更改运行效果的有状态功能组件中使用默认 props?
我不知道这是否有资格获得答案,但您可以通过在应用程序中将您的默认值声明为常量来解决您的所有问题。这意味着;
const Parent = () => {
const somethingUndefined;
return (
<>
<Child prop={somethingUndefined} />
</>
);
};
const Child = ({ prop = {a: 1} }) => {
const [x, setX] = React.useState(1);
React.useEffect(() => {
setX(x + 1);
}, [prop]);
return <div>{x}, {prop.a}</div>;
};
您可以将上面的代码更改为
const Parent = () => {
const somethingUndefined;
return (
<>
<Child prop={somethingUndefined} />
</>
);
};
const defaultPropValue = {a: 1};
const Child = ({ prop = defaultPropValue }) => {
const [x, setX] = React.useState(1);
React.useEffect(() => {
setX(x + 1);
}, [prop]);
return <div>{x}, {prop.a}</div>;
};
这不会导致任何无限循环。
区别赌注这两者:- 在第一个中,prop
初始化为一个新值,即,{a: 1}
,在每次状态更新时,这将是一个新对象(新对象将位于新的内存位置(,它再次调用回调。
在第二个中,我们初始化并将{a: 1}
分配给不会更改的defaultPropValue
。然后,我们将此defaultPropValue
分配给prop
,以便在每次重新渲染时,分配给prop
的值将相同(或来自相同的内存位置(。所以它按预期工作。
希望这个想法很清楚!
useEffect()
将首次运行,然后调用setX()
:
setX()
将更新x
的状态,这将触发组件再次重新渲染。prop
将收到一个新对象const Child = ({ prop = {a: 1} }) => {
useEffect()
将再次运行并调用setX()
整个过程再次重复,这会导致无限循环。
相反,您可以将默认值传递给a
属性并在useEffect()
依赖项数组中使用它
const Parent = () => {
let somethingUndefined; // babel complains if we use `const` without value
return (
<div>
<Child prop={somethingUndefined} />
<Child prop={{ a: 3 }} />
</div>
);
};
const Child = ({ prop = {} }) => {
const { a = 1 } = prop;
const [x, setX] = React.useState(1);
React.useEffect(() => {
setX(x + 1);
}, [a]);
return <div>{x}, {a}</div>;
};
ReactDOM.render(<Parent />, document.getElementsByTagName('body')[0]);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.11.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script>
请参阅 https://codepen.io/McKabue/pen/dyPxGLQ?editors=0010
const Parent = () => {
const somethingUndefined = undefined;
return <Child prop={somethingUndefined}/>;
};
const Child = ({ prop = {a: 1} }) => {
const [x, setX] = React.useState(1);
React.useEffect(() => {
setX(prop.a + 1);
});
return <div>{x}, {prop.a}</div>;
};
ReactDOM.render(
<Parent />,
document.getElementById('root')
);
useEffect(() => {
// anything you want to do
, [JSON.stringify(dependencyName)]}