修复未重新渲染的子组件的方法(由于数据的更改是作为props而不是状态传递的)?

Mia*_*a W 6 javascript meteor reactjs react-datepicker react-lifecycle

背景

我正在使用一个使用ReactJS作为渲染库的Meteor应用程序.

目前,我在更新数据时重新渲染子组件时遇到问题,即使父级正在访问更新的数据并且据称将其传递给子级.

Parent组件是一个数据表.子组件是单击编辑日期字段.

它(理论上)的工作方式:父组件将日期的现有数据作为prop传递给子组件.子组件获取现有的道具数据,处理它并使用它设置一些状态,然后有2个选项:

  • 默认:显示数据
  • 如果用户点击数据字段:更改为输入并允许用户选择日期(使用react-datepicker),更改状态 - 当用户在字段外单击时,触发返回仅显示并将更新的数据从状态保存到数据库

我在表中每行使用子组件两次,每次使用它时,它需要访问数据库中的最新日期数据.因此,如果数据在一个字段中更改,则第二个字段应反映该更改.

问题

除非我手动刷新页面并强制子组件使用新数据进行渲染,否则第二个字段不会反映数据库中的更改.编辑的字段反映数据的变化,因为它反映了什么是存储在状态.

阅读完React文档之后,我确定问题是因为日期是作为一个prop进入,然后作为一个状态处理 - 并且因为该组件不会从prop更改中重新呈现.

我的问题

我该怎么做才能解决这个问题?

我对文档所做的所有阅读强烈建议远离诸如forceUpdate()和getDerivedStateFromProps()之类的东西,但结果我不确定如何通过我想要的方式传递数据.

思考?

我的守则

我已经将代码缩减了一点并删除了特定于我的项目的变量名称,但是如果它有帮助我可以提供更多的实际代码.我认为我的问题比直接调试更具概念性.

ParentComponent () {
    //Date comes as a prop from database subscription
    var date1 = this.props.dates.date1 
    var date2 = this.props.dates.date2
    return(
        <ChildComponent 
            id='handle-date-1'
            selected={[date1, date2]} />
        <ChildComponent 
            id='handle-date-2'
            selected={[date1, date2]} />
    )
}
Run Code Online (Sandbox Code Playgroud)

儿童

ChildComponent() {
    constructor(props) {
        super(props);
        this.state = {
            date1: this.props.selected[0],
            date2: this.props.selected[1],
            field: false,
        };
    }

    handleDatePick() {
        //Handles the event listeners for clicks in/out of the div, handles calling Meteor to update database.
    }
    renderDate1() {
        return(
            <div>
                {this.state.field == false &&
                    <p onClick={this.handleClick}>{formatDate(this.state.date1)}</p>}
                {this.state.field == true &&
                    <DatePicker
                        selected={this.state.date1}
                        startDate={this.state.date1}
                        onChange={this.handleDatePick}
                    />
                }
            </div>
        )
    }
    renderDate2() {
        return(
            <div>
                {this.state.field == false &&
                    <p onClick={this.handleClick}>{formatDate(this.state.date2)}</p>}
                {this.state.field == true &&
                    <DatePicker
                        selected={this.state.date2}
                        startDate={this.state.date2}
                        onChange={this.handleDatePick}
                    />
                }
            </div>
        )
    }
    render() {
        return(
            //conditionally calls renderDate1 OR renderDate2
        )
    }
}
Run Code Online (Sandbox Code Playgroud)

(如果这个代码/我的解释很粗糙,那是因为我还是一个相当初学者/低级别的开发人员.我没有接受过正式培训,所以我在学习一个非常困难的应用程序时正在学习.独唱开发者.这是一个很长的故事.请温柔!)

dee*_*sun 4

React 文档有一节介绍constructor(). 阅读该内容,密切注意黄色突出显示的“注意”部分,应该可以阐明您遇到的确切问题。

本质上,constructor()仅运行一次,最常用于初始化内部/本地状态(或绑定方法)。这意味着您的子组件正在设置date1,并在为该子组件调用使用您的propdate2中的值。无论调用时 is的值是什么,都将被设置为子级的状态,并且即使该值继续更改也将保持不变。selectedconstructor()selectedconstructor()

因此,selected传递给子组件的 prop的任何连续更新都不会反映在该组件的内部状态中,这意味着该组件不会重新渲染。您需要在子组件中的其他位置使用 React 的setState()方法来正确更新该子组件的状态并触发重新渲染。

使用 React 生命周期方法的组合来正确更新子组件是正确的方法。componentDidMount()下面的代码片段为您提供了有关在和中的实现中需要更改哪些内容的主要想法componentDidUpdate()

class Child extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      date1: 0,
      date2: 0,
    };
  }

  /*
    Any time the child mounts to the DOM, 
    you can use the method below to set 
    your state using the current value of your 
    selected prop from the parent component...
  */

  componentDidMount() {
    this.setState({
      date1: this.props.selected[0],
      date2: this.props.selected[1]
    });
  }

  /* 
   When the child receives an update from its parent, 
   in this case a new date selection, and should 
   re-render based on that update, use the method below 
   to make a comparison of your selected prop and then 
   call setState again...
  */

  componentDidUpdate(prevProps) {
    if (prevProps.selected !== this.props.selected) {
      this.setState({
        date1: this.props.selected[0],
        date2: this.props.selected[1]
      });
    }
  }

  render() {
    const { date1, date2 } = this.state;
    return (
      <div style={{ border: `4px dotted red`, margin: 8, padding: 4 }}>
        <h1 style={{ fontWeight: 'bold' }}>Child</h1>
        <h2>The value of date1 is {date1}</h2>
        <h2>The value of date2 is {date2}</h2>
      </div>
    );
  }
}

class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      valOne: 0,
      valTwo: 0
    };
  }

  incrementOne = () => {
    this.setState(prevState => ({ valOne: (prevState.valOne += 1) }));
  };

  incrementTwo = () => {
    this.setState(prevState => ({ valTwo: (prevState.valTwo += 1) }));
  };

  render() {
    const { valOne, valTwo } = this.state;
    return (
      <div style={{ border: `4px solid blue`, margin: 8, padding: 4 }}>
        <h1 style={{ fontWeight: 'bold', fontSize: 18 }}>Parent</h1>
        <button onClick={() => this.incrementOne()}>Increment date1</button>
        <button onClick={() => this.incrementTwo()}>Increment date2</button>
        <Child selected={[valOne, valTwo]} />
      </div>
    );
  }
}

ReactDOM.render(<Parent />, document.querySelector('#app'));
Run Code Online (Sandbox Code Playgroud)
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
Run Code Online (Sandbox Code Playgroud)

由于您正在为您的孩子使用 React 组件模式,因此充分利用 React 生命周期方法将真正帮助您。我强烈建议你学习React 组件生命周期。当您继续使用 React 时,在这种情况下它将变得至关重要。componentDidMount()都是componentDidUpdate()很好的起点。

希望这有帮助。让我们知道结果如何。