Как исправить дочерний компонент, который не выполняет повторную визуализацию (из-за изменения данных, передаваемых как реквизиты, а не как состояние)? - PullRequest
0 голосов
/ 11 октября 2018

Фон

Я работаю над приложением Meteor, которое использует ReactJS в качестве библиотеки рендеринга.

В настоящее время у меня возникают проблемы с повторным использованием дочернего компонента.-отобразить, когда данные были обновлены, даже если родительский объект обращается к обновленным данным и предположительно передает их дочернему элементу.

Родительский компонент представляет собой таблицу данных.Дочерний компонент является полем даты, доступным для редактирования.

Принцип его работы (теоретически) : Родительский компонент передает существующие данные для даты в дочерний компонент в качестве реквизита.Дочерний компонент берет эти существующие реквизиты, обрабатывает их и устанавливает некоторые состояния, используя их, а затем имеет 2 варианта:

  • по умолчанию: отображать данные
  • , если пользователь нажимает на поле данных: изменить навводить и разрешать пользователю выбирать дату (с помощью response-datepicker), изменяя состояние - когда пользователь щелкает за пределами поля, запускает возврат только для отображения и сохраняет обновленные данные из состояния в базу данных

Я использую дочерний компонент дважды в строке в таблице, и каждый раз, когда он используется, ему требуется доступ к самым последним данным даты из базы данных.Таким образом, если данные изменяются в одном поле, второе поле должно отражать это изменение.

Проблема

Второе поле не отражаетизменения в базе данных, если я не обновлю страницу вручную и не заставлю дочерний компонент отображаться с новыми данными.Отредактированное поле - это , отражающее изменение данных, потому что оно отражает то, что хранится в состоянии.

После прочтения документации React, я уверен, что проблема в том, что дата появляется в качестве опоры,затем обрабатываются как состояние - и потому что компонент не будет перерисовываться после смены реквизита.

Мой вопрос

Что мне делать спочини это?

Все прочитанные мной документы настоятельно рекомендуют держаться подальше от таких вещей, как 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]} />
    )
}

Ребенок

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
        )
    }
}

(Если этот код / ​​мое объяснение является грубым,это потому, что я все еще начинающий / низкоуровневый разработчик. У меня нет формального обучения, поэтому я учусь на работе, работая над довольно сложным приложением. Как одиночный разработчик. Это долгая история.нежный!)

1 Ответ

0 голосов
/ 11 октября 2018

В документах React есть раздел об использовании лучших практик constructor().Читая это, обращая пристальное внимание на выделенный желтым цветом раздел «Примечание», следует осветить точную проблему, с которой вы столкнулись.

По сути, constructor() запускается только один раз и чаще всего используется для инициализировать внутреннее / локальное состояние (или методы привязки).Это означает, что ваш дочерний компонент устанавливает date1 и date2 со значениями из вашего selected prop , один раз , когда для этого потомка вызывается constructor().Независимо от значения selected, когда вызывается constructor(), будет установлено состояние ребенка, и оно останется неизменным, даже если значение продолжит изменяться.

Таким образом, любые последующие обновления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'));
<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>

Поскольку вы используете шаблон React Component для своих детей, хорошее использование методов жизненного цикла React действительно поможет вам в этом.Я не могу убедительно предложить вам изучить жизненный цикл компонента React .По мере того, как вы продолжаете использовать React, это станет необходимым в таких случаях, как этот.componentDidMount() и componentDidUpdate() - хорошие места для начала.

Надеюсь, что это поможет.Дайте нам знать, как это получается.

...