Как получить доступ к состоянию ребенка в React?

152

У меня есть следующая структура:

FormEditor - содержит несколько FieldEditor FieldEditor - редактирует поле формы и сохраняет в нем различные значения. Состояние

При нажатии кнопки в FormEditor я хочу иметь возможность собирать информацию о полях из всех компонентов FieldEditor, информацию, которая находится в их состоянии, и иметь все это в FormEditor.

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

Не могу ли я просто получить доступ к состоянию детей? Идеально?

  • 4
    «Разве я не могу просто получить доступ к состоянию детей? Это идеально?» Нет. Государство является чем-то внутренним и не должно просачиваться наружу. Вы можете реализовать методы доступа для вашего компонента, но даже это не идеально.
  • 0
    @FelixKling Тогда вы предполагаете, что идеальный способ общения ребенка с родителем - это только события?
Показать ещё 3 комментария
Теги:

3 ответа

92
Лучший ответ

Если у вас уже есть обработчик onChange для отдельных FieldEditors, я не понимаю, почему вы не могли просто переместить состояние в компонент FormEditor и просто передать обратный вызов оттуда в FieldEditors, который обновит родительское состояние. Мне кажется, это более эффективный способ сделать это.

Возможно, что-то в этом роде:

class FieldEditor extends React.Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
  }

  handleChange(event) {
    const text = event.target.value;
    this.props.onChange(this.props.id, text);
  }

  render() {
    return (
      <div className="field-editor">
        <input onChange={this.handleChange} value={this.props.value} />
      </div>
    );
  }
}

class FormEditor extends React.Component {
  constructor(props) {
    super(props);
    this.state = {};

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

  handleFieldChange(fieldId, value) {
    this.setState({ [fieldId]: value });
  }

  render() {
    const fields = this.props.fields.map(field => (
      <FieldEditor
        key={field}
        id={field}
        onChange={this.handleFieldChange}
        value={this.state[field]}
      />
    ));

    return (
      <div>
        {fields}
        <div>{JSON.stringify(this.state)}</div>
      </div>
    );
  }
}

// Convert to class component and add abillity to dynamically add/remove fields by having it in state
const App = () => {
  const fields = ["field1", "field2", "anotherField"];

  return <FormEditor fields={fields} />;
};

ReactDOM.render(<App />, document.body);

http://jsbin.com/qeyoxobixa/edit?js,output

Редактировать: Вместо того, чтобы передавать элементы FieldEditor как дочерние в FormEditor, я просто передаю список fieldIds и создаю их вместо этого в FormEditor. Это упрощает динамическое добавление FieldEditor и делает метод визуализации FormEditor менее удачным.

  • 2
    Это полезно для onChange, но не так много для onSubmit.
  • 1
    @ 190290000RubleMan Я не уверен, что вы имеете в виду, но поскольку обработчики onChange для отдельных полей гарантируют, что у вас всегда есть полные данные формы, доступные в вашем состоянии в компоненте FormEditor, ваш onSubmit будет просто включать что-то вроде myAPI.SaveStuff(this.state); , Если вы уточните немного больше, может быть, я могу дать вам лучший ответ :)
Показать ещё 2 комментария
144

Непосредственно перед тем, как я подробно расскажу о том, как вы можете получить доступ к состоянию дочернего компонента, обязательно прочитайте ответ Markus-ipse относительно лучшего решения для этого конкретного сценария.

Если вы действительно хотите получить доступ к состоянию дочерних компонентов, вы можете назначить свойство ref с именем каждого дочернего элемента. Теперь есть два способа реализации ссылок: использование React.createRef() и обратного вызова refs.

Использование React.createRef()

В настоящее время это рекомендуемый способ использования ссылок с React 16.3 (дополнительную информацию см. В документации). Если вы используете более раннюю версию, то смотрите ниже ссылки обратного вызова.

Вам нужно будет создать новую ссылку в конструкторе вашего родительского компонента, а затем назначить ее дочернему элементу через атрибут ref.

class FormEditor extends React.Component {
  constructor(props) {
    super(props);
    this.FieldEditor1 = React.createRef();
  }
  render() {
    return <FieldEditor ref={this.FieldEditor1} />;
  }
}

Чтобы получить доступ к этому виду ссылки, вам нужно использовать:

const currentFieldEditor1 = this.FieldEditor1.current

Это вернет экземпляр смонтированного компонента, поэтому вы можете использовать currentFieldEditor1.state для доступа к состоянию.

Просто заметьте, что если вы используете там ссылки на узле DOM вместо компонента (например, <div ref={this.divRef}/>), то this.divRef.current вернет базовый элемент DOM вместо компонента пример.

Обратный звонок

Это свойство принимает функцию обратного вызова, которой передается ссылка на присоединенный компонент. Этот обратный вызов выполняется сразу после монтирования или демонтажа компонента.

Например:

<FieldEditor
    ref={(fieldEditor1) => {this.fieldEditor1 = fieldEditor1;}
    {...props}
/>

В этих примерах ссылка хранится на родительском компоненте. Чтобы вызвать этот компонент в своем коде, вы можете использовать:

this.fieldEditor1

а затем используйте this.fieldEditor1.state чтобы получить состояние.

Прежде всего, убедитесь, что ваш дочерний компонент отрендерен, прежде чем пытаться получить к нему доступ ^ _ ^

Дальнейшая информация

Если вы хотите узнать больше о недвижимости React, проверьте эту страницу на Facebook.

Обязательно прочитайте раздел " Не злоупотребляйте ссылками ", в котором говорится, что вы не должны использовать дочернее state чтобы "что-то происходило".

Надеюсь, это поможет ^ _ ^

Редактировать: Добавлен React.createRef() для создания ссылок. Удален код ES5.

  • 5
    Также аккуратный маленький кусочек, вы можете вызывать функции из this.refs.yourComponentNameHere . Я нашел это полезным для изменения состояния с помощью функций. Пример: this.refs.textInputField.clearInput();
  • 7
    Остерегайтесь (из документации): Refs является отличным способом , чтобы отправить сообщение конкретного экземпляр ребенка таким образом , что будет неудобно делать через потоковые Реактивный props и state . Однако они не должны быть вашей основной абстракцией для передачи данных через ваше приложение. По умолчанию используйте Поток данных Reactive и сохраняйте ref для случаев использования, которые по своей природе не являются реактивными.
Показать ещё 3 комментария
2

Теперь вы можете получить доступ к состоянию InputField, которое является дочерним по отношению к FormEditor.

По сути, всякий раз, когда происходит изменение состояния поля ввода (дочернего), мы получаем значение из объекта события и затем передаем это значение в Parent, где устанавливается состояние в Parent.

При нажатии кнопки мы просто печатаем состояние полей ввода.

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

class InputField extends React.Component{
  handleChange = (event)=> {
    const val = event.target.value;
    this.props.onChange(this.props.id , val);
  }

  render() {
    return(
      <div>
        <input type="text" onChange={this.handleChange} value={this.props.value}/>
        <br/><br/>
      </div>
    );
  }
}       


class FormEditorParent extends React.Component {
  state = {};
  handleFieldChange = (inputFieldId , inputFieldValue) => {
    this.setState({[inputFieldId]:inputFieldValue});
  }
  //on Button click simply get the state of the input field
  handleClick = ()=>{
    console.log(JSON.stringify(this.state));
  }

  render() {
    const fields = this.props.fields.map(field => (
      <InputField
        key={field}
        id={field}
        onChange={this.handleFieldChange}
        value={this.state[field]}
      />
    ));

    return (
      <div>
        <div>
          <button onClick={this.handleClick}>Click Me</button>
        </div>
        <div>
          {fields}
        </div>
      </div>
    );
  }
}

const App = () => {
  const fields = ["field1", "field2", "anotherField"];
  return <FormEditorParent fields={fields} />;
};

ReactDOM.render(<App/>, mountNode);
  • 3
    Не уверен, что смогу поддержать это. Что вы упоминаете здесь, на который еще не ответил @ Markus-ipse?

Ещё вопросы

Сообщество Overcoder
Наверх
Меню