【问题标题】:Why getDerivedStateFromProps does not allow to re-render with the state update? Not a problem for componentWillReceiveProps - ReactJS为什么 getDerivedStateFromProps 不允许使用状态更新重新渲染? componentWillReceiveProps 不是问题 - ReactJS
【发布时间】:2021-08-04 04:41:48
【问题描述】:

我正在尝试从 componentWillReceiveProps 转移到 getDerivedStateFromProps,在某些情况下,我是成功的,但是当案例是将道具附加到现有状态时,事情开始变得不一样了。当对组件的状态进行更新时,状态会发生变化(并且组件在更新后也会发生变化),但仍会呈现先前的状态。使用 getDerivedStateFromProp 而不是 componentWillReceiveProps 时会发生一些奇怪的事情。 似乎该方法不能很好地处理“内部”更改。在下面的示例中,我在 Child 上有 getDerivedStateFromProp 并且它可以工作,但因为只是渲染道具。在一个更简单的示例中也观察到了这种行为,其中我没有任何子组件并且只是呈现状态更改。

下面的代码显示了一个子组件,用于打印/显示 props 接收到的数据,同时使用删除数据处理程序(从子组件交互中删除存储在 Parent 的数据)。使用getDerivedStateFromProps() 时,我无法访问this.state,并且prevState 并不意味着相同,因为状态是累积的。当我从子组件中删除数据时,不会更新子组件的道具(使用 componentWillReceiveProps 时可以)。所以,我找不到替代我的UNSAFE_componentWillReceiveProps

componentWillReceiveProps:


  UNSAFE_componentWillReceiveProps(nextProps){
    this.setState({
      data: [...this.state.data,...nextProps.data]
    })
  }

getDerivedStateFromProps:

   static getDerivedStateFromProps(nextProps,state) {

    if (!isEqual(nextProps.data, state.data)) {
      return {
        data: [...state.data, ...nextProps.data]
      };
    }
    return null;
  }


按预期工作的原始代码(在父组件上的 getDerivedStateFromProps 之前)

DataConsole - 父组件:

export class DataConsole extends Component {
  // Used for unsubscribing when our components unmount
  unsub = null;

  static defaultProps = {
    data: [],
  };

  constructor(props) {
    super(props);
    this.state = {
      data: [],
    };

    this.handleTableRowClick = this.handleTableRowClick.bind(this);
  }


   UNSAFE_componentWillReceiveProps(nextProps){
    this.setState({
      data: [...this.state.data,...nextProps.data]
    })
  }

  handleTableRowClick(key) {
    console.log(
      "handleTable",
      key,
      this.state.data[key],
      this.state.data.length
    );
     const e = this.state.data.splice(key, 1)
     //console.log("remove?", e , this.state.data.length)

    this.setState({
      undoDataRemove: e
    });
  }


  render() {
    return (
      <div>
        <Container
          fluid
          style={{ paddingLeft: 0, paddingRight: 0 }}
          className="DataContainer"
        >
          <Row noGutters>
            <Col sm={8} className="ConsoleTable">
              <div>
                <DataViewer
                  data={this.state.data}
                  rowClickHandler={this.handleTableRowClick}
                />
              </div>

       ...




DataViewer - 子组件

import isEqual from "react-fast-compare";
...

export class DataViewer extends Component {
  static defaultProps = {
    data: [],
  };

  constructor(props){
    super(props)
    this.state={data: []}
  }


  componentDidUpdate() {
    console.log("DataViewer updated");
  }

  static getDerivedStateFromProps(nextProps, prevProps) {

    console.log(nextProps, prevProps)
    if (!isEqual(nextProps.data, prevProps.data)) {
      return {
        data: nextProps.data
      };
    }
    return null;
  }


  render() {
    return (
      <div className={"TableData"}>
        <Table responsive="lg" striped borderless hover>
          <tbody>
            {this.state.data.map((elem, ids) => {
              if (!isEmpty(elem)) {
                return (
                  <tr key={ids} onClick={() => this.props.rowClickHandler(ids)}>
                    <td>{ids + 1}</td>
                    {Object.keys(elem).map(function (value, idx) {
                      return (
                        <td key={idx}>
                          {value}:{elem[value]}
                        </td>
                      );
                    })}
                  </tr>
                );
              } else {
                return null;
              }
            })}
          </tbody>
        </Table>
      </div>
    );
  }
}


【问题讨论】:

  • if (!isEqual(nextProps.data, state.data)) {你可能错过了state.data
  • 谢谢@Sundar,我会检查是否只是这个错字造成了麻烦。 (更新)不幸的是只是一个复制粘贴错误
  • 您是否仍然面临该问题或已解决
  • 仍然面临同样的问题。 state.data 的缺失只是原版的复制粘贴错误。
  • 有了这些数据,我无法为您提供帮助...需要更多关于 nextProps.datastate.data 的信息

标签: javascript reactjs state


【解决方案1】:

您的代码中存在导致您的问题的错误,它与getDerivedStateFromPropsUNSAFE_componentWillReceiveProps 无关。

故障线是这样的:

const e = this.state.data.splice(key, 1) 

它在不调用 setState 的情况下更改 this.state.data。永远不要那样做。唯一允许您对this.state 进行任何更改的方法是通过this.setState 或通过从getDerivedStateFromProps 返回要合并到状态的内容,而不是任何其他方式。

为了更轻松地应对状态树深处的变化,不可变库可以派上用场。 immer 目前是该类别中的顶级候选人之一。使用immer,您可以随意修改state,只要您使用this.setState(produce(this.state, newState =&gt; { /* change newState here */ })) 模式将其包装到produce 调用中即可:

import produce from 'immer';

// ...

this.setState(produce(this.state, newState => {
  const e = newState.data.splice(key, 1);
  newState.undoDataRemove = e;
}));

希望,这会有所帮助。

【讨论】:

  • 嗨@Domi,我明白你的意思。我在data.splice 之后使用 setState 来触发组件更新,如果我在componentDidUpdate() 范围内看到更改。但它与某些东西有关,因为如果我坚持 UNSAFE_componentWillReceiveProps ,这不会显示为问题。但是,感谢您的洞察力,我将更多地使用和探索不可变库的使用,immer 看起来很酷!
  • 如果您按照自己的方式进行操作,isEqual(nextProps.data, state.data) 将变为 true,因此您的派生状态将无法按预期工作。您认为这是安全的,因为您使用setState 跟进它并尝试使用那些自定义挂钩来包装它,但是尝试更改state 而不创建新值(级联)是在自找麻烦和错误,尤其是在兼容性方面。 React 世界中与状态相关的大多数库只会进行浅层比较,甚至不允许您更改比较函数。
猜你喜欢
  • 2022-11-06
  • 1970-01-01
  • 2016-04-22
  • 2020-11-04
  • 1970-01-01
  • 1970-01-01
  • 2021-03-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多