有状态(使用钩子)功能组件中的默认道具

Man*_*avM 6 javascript reactjs react-hooks

我在 SO 上遇到了几个关于功能组件的默认 props 的问题,他们都推荐使用 ES6 默认参数。以下是这些问题的链接。


但是,当我使用该方法编写具有在 props 更改上运行的效果的组件时,我会遇到非基元的不良行为。例如,以下代码将导致无限循环。

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]);
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)


我尝试了两种尝试规避问题的方法。首先,只需分配一个包含默认值的不同变量,并将未修改的 prop 放入依赖数组中。IE

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>;
};
Run Code Online (Sandbox Code Playgroud)

另一种方法是使用类似的东西(prop || {a:1})代替prop你使用它的任何地方,除了在依赖数组中。IE

const Child = ({ prop }) => {
  const [x, setX] = React.useState(1);

  React.useEffect(() => {
    setX(x + 1);
  }, [prop]);

  return <div>{x}, {(prop || {a: 1}).a}</div>;
};
Run Code Online (Sandbox Code Playgroud)

但是这两种解决方案似乎都不是最理想的,因为它需要大量浪费精力(和庞大的代码)。

defaultProps也是无限循环问题的解决方案,但已弃用。请注意,此 rfc 中提供的示例在代码中也使用了 ES6 默认参数。

我错过了什么吗?是否有更好的方法在对 props 更改运行影响的有状态功能组件中使用默认 props?

Ans*_*Ali 8

我不知道这是否符合回答的条件,但可以通过在应用程序中将默认值声明为常量来解决您的所有问题。这意味着;

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>;
};
Run Code Online (Sandbox Code Playgroud)

您可以将上面的代码更改为

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>;
};
Run Code Online (Sandbox Code Playgroud)

这不会导致任何无限循环。

这两者的区别在于:- 首先,prop初始化为一个新值,即,{a: 1}在每次状态更新时,这将是一个新对象(新对象将位于新的内存位置),并调用回调再次。

在第二,我们初始化并分配{a: 1}defaultPropValue这不会改变。然后我们将它分配defaultPropValue给,prop以便在每次重新渲染时,分配给 的值prop将相同(或来自相同的内存位置)。所以它按预期工作。

希望这个想法很清楚!


Muh*_*eeb -2

useEffect(() => {
// anything you want to do
, [JSON.stringify(dependencyName)]}
Run Code Online (Sandbox Code Playgroud)

  • 根据 [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) `JSON.stringify` 不会保留对象内的键顺序。这意味着您可能会遇到无限循环,因为字符串会由于不同的键顺序而不断变化。 (2认同)