реагировать на иерархию компонента с родительским состоянием удержания

Я пытаюсь создать React SPA для следующего случая: School -Student1 -Student2 -Student3 -name -Address - номер дома, улица, город, штат, страна.

Если я считаю, «Школа», «Студент», «Адрес» реагирующими компонентами. Школа: содержит / отображает список учеников, которых можно добавлять и удалять. Учащийся: содержит / отображает информацию, такую ​​как имя, адрес, которую можно изменить. Адрес: содержит / отображает информацию об адресе, которую можно изменить.

Теперь, как я могу здесь управлять состоянием, скажем, если пользователь изменяет адрес одного из учеников, как это может отразиться на школе.

варианты, о которых я мог подумать: 1. использовать Redux, но скажу, что я хочу создать эту школу как компонент, который будет использоваться в другом приложении для реагирования, смогу ли я использовать хранилище redux на уровне компонентов.

  1. передайте объект Student компоненту реакции Student, который обновит его информацией при изменении .. изменение свойств.

Примечание: я новичок в реакции, извините, что я написал неправильно или не имеет смысла.

import * as React from 'react';

export default class School extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      schoolName: "test school",
      students: [],
      update:false
    }
  }

  handleAddStudent = ()=>{
    let student = {
      name : "student1",
      className: "class1",
      address:{
        houseNumber :  "1-2/A-3",
        street: "street1",
        city: "city1",
        country: "country1"
      }
    }
    this.state.students.push(student);
    this.setState({ update: true});
  }

  handleToJson = () => {
    console.log(JSON.stringify(this.state));
  }

  render() {
    return (
    <div>
      <ul>{
        this.state.students.map((student)=>{
            return <Student student={student}/>
        })
      }
      </ul>
      <button onClick={this.handleAddStudent}>Add Student </button>
      <br/>
        <button onClick={this.handleToJson}>To json</button>
    </div>
    );
  }
}

export class Student extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      studentName: this.props.student.name,
      className: this.props.student.className,
      address: this.props.student.address
    }
  }

  handleChange = (e)=>{
    const value = e.target.value;
    switch(e.target.name)
    {
      case 'studentName':
        this.setState({ studentName: value});
        this.props.student.name = value;
        break;
      case 'className':
        this.setState({ className: value });
        this.props.student.className = value;
        break;
    }
  }

  render() {
    return (
      <div>
        <input name='studentName' value={this.state.studentName} onChange={this.handleChange} />
        <input name='className' value={this.state.className} onChange={this.handleChange} />
        <Address name='address' address={this.state.address}/>
      </div>
    );
  }
}

export class Address extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      houseNumber: this.props.address.houseNumber,
      street: this.props.address.street,
      city: this.props.address.city,
      country: this.props.address.country
    }
  }

  handleChange = (e) => {
    const value = e.target.value;
    switch (e.target.name) {
      case 'houseNumber':
        this.setState({ houseNumber: value });
        this.props.address.houseNumber = value;
        break;
      case 'street':
        this.setState({ street: value });
        this.props.address.street = value;
        break;
      case 'city':
        this.setState({ city: value });
        this.props.address.city = value;
        break;
      case 'country':
        this.setState({ country: value });
        this.props.address.country = value;
        break;
    }
  }

  render() {
    return (
      <div>
        <input name='houseNumber' value={this.state.houseNumber} onChange={this.handleChange} />
        <input name='street' value={this.state.street} onChange={this.handleChange} />
        <input name='city' value={this.state.city} onChange={this.handleChange} />
        <input name='country' value={this.state.country} onChange={this.handleChange} />
      </div>
    );
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

Спасибо


person TheCoder    schedule 11.02.2019    source источник
comment
Если вы планируете полностью повторно использовать компонент в другом приложении, вам придется предоставить этому приложению собственное управление состоянием. Что касается параметров управления состоянием, у вас есть контекстный api, передаваемый через props и redux. Redux, вероятно, станет излишним для всего, что не является очень большим и сложным приложением.   -  person Keno Clayton    schedule 11.02.2019
comment
Я обновил сообщение с фрагментом кода, где родительский объект предоставляет объект Student в качестве реквизита для дочерних и дочерних обновлений всякий раз, когда пользователь изменяется. Но я, согласно рекомендациям по реагированию, это неправильный подход (изменение в дочернем) .... могу ли я узнать подробности в 'у вас есть контекстный api, проходящий через реквизиты и редукцию' ..   -  person TheCoder    schedule 11.02.2019
comment
Я напишу ответ в ближайшее время   -  person Keno Clayton    schedule 11.02.2019


Ответы (1)


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

Теперь, как я могу здесь управлять состоянием, скажем, если пользователь изменяет адрес одного из учеников, как это может отразиться на школе.

В зависимости от вашего варианта использования было бы наиболее эффективно хранить список студентов и их свойств внутри компонента <School />. Таким образом, вы можете передать его по мере необходимости, не беспокоясь.

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

Школьный компонент (оставить как есть)

Школа должна вести список учеников, а также все их вспомогательные свойства.

Компоненты "Студент и адрес"

Эти двое всегда должны ссылаться на свои реквизиты и вызывать родительский метод для обновления состояния.

Вместо того

this.state = {
  studentName: this.props.student.name,
  className: this.props.student.className,
  address: this.props.student.address
}

просто делать

const { name, className, address } = this.props.student;

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

Вам следует переместить свои функции типа handleX в School и передать его через свойства.

Вы также можете пропустить использование слишком большого количества свойств и просто использовать Context API. Например...

//In parent

const ThemeContext = React.createContext('default value');

...
return (
  <SchoolContext.Provider value={this.state}>
    ...
  </SchoolContext.Provider>
)
...

//In any child
import Context from './School';

class ChildComponent extends React.Component {
  ...
}

ChildComponent.contextType = Context;

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

person Keno Clayton    schedule 12.02.2019
comment
Если я не поддерживаю локальное состояние в «Студент» и «Адрес», тогда всякий раз, когда пользователь изменяет значение в студенте или адресе, который он не принимает, или если я изменяю состояние в родительском элементе, это в конечном итоге приведет к повторному рендерингу всей школы. компонент вместо рендеринга ученика / адреса, который когда-либо менялся, не так ли? .. еще одна вещь, если нужно переместить handleX в школу, тогда я бы переместил handleX из Student и Address и сказал, есть ли еще один вложенный компонент в Address для это тоже. - person TheCoder; 12.02.2019
comment
А также, если я перемещу handleX из Student в школу, тогда я бы в конечном итоге предоставил измененный objectId 'student', имя поля и значение в методе handleX, верно? - person TheCoder; 12.02.2019
comment
@akira По умолчанию да, он будет повторно отображать дочерние компоненты, если они зависят от этих свойств. Однако вы можете использовать shouldComponentUpdate для сравнения значений и предотвращения обновлений, когда в этом нет необходимости. Я также думаю, что ваша функция handleX должна просто обновлять родительское состояние, в которое вы можете легко передавать вложенные свойства. Просто клонируйте родительское состояние в дочерних элементах, а затем объедините его после внесения изменений. - person Keno Clayton; 12.02.2019