如何在道具变更时从头开始强制React(使用挂钩的功能组件)创建新组件?

时间:2019-03-27 17:31:30

标签: reactjs react-hooks

TLDR

是否有一种方法可以迫使React从头开始重新创建组件(使用钩子功能组件),而不是在prop更改时重新呈现它?

编辑

键属性确实起作用。我遇到的问题是由于我正在执行的状态更新序列。第一次更新正在尝试立即呈现需要完成最后一次更新的组件。


我正在开发一个名为<AddProductWidget>的组件,它将获得有关产品的以下props信息,并且需要显示其详细信息。它属于带有搜索栏的页面,您需要输入产品ID并从云功能获取产品详细信息。

它呈现如下:

  <AddProductWidget
    userImages={props.productDetails.userImages}    // Array of strings (urls)
    title={props.productDetails.title}
    stars={props.productDetails.stars}
  />

AddProductWidget.js

它呈现一个<UserImages>组件,该组件使用户可以在保存到数据库之前单击一些图像进行选择。因此,它需要一个state才能“记住”用户单击了哪些图像。

function AddProductWidget(props) {

  const [userImagesSelected, setUserImagesSelected] = useState(
    () => {
      if (props.userImages && props.userImages > 0) {
        return new Array(props.userImages.length).fill(false);
      }
      return null;
    }
  );

  // IT renders the <UserImages> component
  // The code here is simplified

  return(
      <UserImages
        userImages={props.userImages}
        userImagesSelected={userImagesSelected}
        setUserImagesSelected={setUserImagesSelected}
      />
  );

}

该数组userImagesSelected的初始状态应与userImages数组的长度相同,并用false值填充,一旦用户开始,它将变为true单击图像。如果产品没有用户图像,则props.userImages将是nulluserImagesSelected也应该是null

当我加载第一个产品时,一切都按预期工作。无论是否有图像,一切都可以正确显示。

问题:

问题是,例如,当我加载一个产品的图像为零时,随后又加载了另一个产品的图像为5个。基本上props的{​​{1}}会发生变化(因为它收到了新产品),但是由于React似乎正在尝试使用相同的组件实例,所以我得到了一个错误,因为我的AddProductWidget组件将尝试呈现选定的图像信息,但是UserImages的状态仍为userImagesSelected,因为最后的产品没有图像。

在使用类时,我可以强制React使用null属性来重新创建组件实例,但这似乎不适用于钩子,因为以下代码无法解决我的问题:

key

问题

是否有一种方法可以迫使React从头开始重新创建组件(使用钩子功能组件),而不是在 <AddProductWidget key={productID} userImages={props.productDetails.userImages} // Array of strings (urls) title={props.productDetails.title} stars={props.productDetails.stars} /> 更改时重新呈现它?

额外

A找到了解决方法,但是即使在React文档中,它似乎也有点怪异。它基本上使用状态来检测prop何时更改,并在渲染期间调用props。我希望从头开始重新创建组件。

Hooks FAQ: How do I implement getDerivedStateFromProps

1 个答案:

答案 0 :(得分:2)

不确定您如何确定其他key不会重新安装组件,但是应该这样做(您确定密钥已更改?)

这是一个正在运行的示例,显示了此

function Test() {
  React.useEffect(() => {
    console.log('Test was mounted');
    return () => {
      console.log('Test was un-mounted');
    };
  }, []);

  return "hi there, I'm Test";
}

function App() {
  const [val, setVal] = React.useState("Change me...");
  return (
    <div className="App">
      <input value={val} onChange={e => setVal(e.target.value)} />
      <br />
      <Test key={val} />
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<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" />