Реагировать: получить состояние дочернего компонента в родительском

У меня есть этот контейнер, и он не находится на одном уровне. Как я могу получить состояние формы, когда я нажимаю кнопку (которая находится на родительском элементе)?

Я создал демонстрацию для решения моей проблемы.

https://codesandbox.io/s/kmqw47p8x7

class App extends React.Component {
  constructor(props) {
    super(props);
  }

  save = () => {
    alert("how to get state of Form?");
    //fire api call
  };
  render() {
    return (
      <div>
        <Form />
        <button onClick={this.save}>save</button>
      </div>
    );
  }
}
  • Я не хочу синхронизировать состояние для события onChange, потому что внутри формы может быть другая форма.

person Melissa94    schedule 23.10.2018    source источник
comment
Переместить кнопку в форму и передать состояние onclick родителю   -  person Just code    schedule 23.10.2018
comment
@Justcode Я хочу, но это не так просто.   -  person Melissa94    schedule 23.10.2018


Ответы (4)


Чтобы получить доступ к дочернему экземпляру из родительского, вам необходимо знать о ref:

Сначала добавьте formRef вверху вашего класса приложения:

formRef = React.createRef();

Затем в рендере приложения передайте ref prop в свой тег формы:

<Form ref={this.formRef} />

Наконец, получите состояние из дочерней формы:

save = () => {
    alert("how to get state of Form?");

    const form = this.formRef.current;
    console.log(form.state)
};

Ознакомьтесь с демонстрацией здесь

person Kenzk447    schedule 23.10.2018
comment
это работает, но вы не должны использовать это. это нарушает инкапсуляцию компонентов. React docs говорит, что не злоупотребляйте ссылками, его следует использовать только для доступа к ссылкам на dom node. - person hannad rehman; 23.10.2018
comment
да, я согласен с тем, чтобы не злоупотреблять отказом. Но в этом случае, чтобы сохранить структуру дерева компонентов, лучше всего использовать ref. - person Kenzk447; 23.10.2018
comment
Это так круто, прослушивание состояния формы не идеально, потому что само состояние формы имеет раскрывающийся список, кучу пользовательских интерактивных материалов или возможную дочернюю форму, этот подход ref просто делает мой код намного чище. - person Melissa94; 23.10.2018

в идеале действие отправки формы принадлежит компоненту Form. Вы можете поместить button в компонент From и передать в форму обратный вызов отправки.

  class App extends React.Component {
  constructor(props) {
    super(props);
  }

   save = (data) => {
     // data is passed by Form component
    alert("how to get state of Form?");
    //fire api call
  };
  render() {
    return (
      <div>
        <Form onFormSubmit={this.save} />
      </div>
    );
  }
}
person hannad rehman    schedule 23.10.2018
comment
Я не контролирую структуру - person Melissa94; 23.10.2018

вы можете написать такой код

https://codesandbox.io/s/23o469kyx0

person masongzhi    schedule 23.10.2018

Как уже упоминалось, ссылка может использоваться для получения экземпляра компонента с сохранением состояния и доступа к состоянию, но это нарушает инкапсуляцию:

<Form ref={this.formRef}/>

Более предпочтительный способ - рефакторинг Form для обработки этого случая, т.е. принять onChange функцию обратного вызова, которая будет запускаться при изменении состояния формы:

<Form onChange={this.onFormChange}/>

Я не хочу синхронизировать состояние для события onChange, потому что внутри формы может быть другая форма.

Формы должны будут справиться с этим любым способом; было бы невозможно достичь вложенной формы по ссылке от дедушки или бабушки. Это могло быть в случае подъема состояния.

Например. в родительском компоненте:

  state = {
    formState: {}
  };

  onFormChange = (formState) => {
    this.setState(state => ({
      formState: { ...state.formState, ...formState }
    }));
  }

  render() {
    return (
      <Form state={this.state.formState} onChange={this.onFormChange} />
    );
  }

По компоненту формы:

  handleChange = e =>
    this.props.onChange({
      [e.target.name]: e.target.value
    });

  render() {
    return (
        <input
          onChange={this.handleChange}
          name="firstName"
          value={this.props.state.firstName}
        />
    );
  }

Вот демонстрация.

person Estus Flask    schedule 23.10.2018
comment
Возвышение состояния должно быть «нормальным» способом, но меня беспокоит форма внутри формы. У меня есть несколько привязок к моей форме и дочерней форме, то есть, когда дочерние элементы из состояния меняются, я должен поднять состояния для нескольких родителей, код выглядит действительно некрасиво. - person Melissa94; 23.10.2018
comment
код выглядит ужасно - это хороший повод использовать библиотеку управления состоянием. Можете ли вы обновить вопрос с помощью кода для вашей формы в форме? Опять же, вы не можете получить доступ к вложенной форме с помощью ссылки. Ссылки часто являются антипаттернами, в данном случае они определенно антипаттернами. - person Estus Flask; 23.10.2018
comment
так я должен это сделать? componentDidUpdate() {this.props.getChildState(this.state)} в форме? - person Melissa94; 23.10.2018
comment
Это не будет отмененным государством. Поднятое состояние означает, что вложенным компонентам не нужны свои собственные this.state; есть один источник истины, и он находится в родительском компоненте. Я обновил ответ для ясности. - person Estus Flask; 23.10.2018
comment
ссылка на демо неверна, я думаю, вы забыли форк. Ну, вы помещаете каждое состояние в родительский, что, если форма имеет динамические строки и сложное взаимодействие с пользователем, такое как перетаскивание? тогда ваш контейнер будет переполнен. - person Melissa94; 23.10.2018
comment
Спасибо, что заметили, я забыл сохранить его после разветвления. Контейнер должен удерживать состояние, но не должен удерживать логику для управления им. Взаимодействия могут оставаться в дочерних компонентах, им просто нужно вернуть обновленное состояние родителю. Так работает однонаправленный поток данных. - person Estus Flask; 23.10.2018