0
votes

I'm trying to create a React SPA for the following case: School -Student1 -Student2 -Student3 -name -Address - house-number, street, city, state, country.

If I consider, School, Student, Address as react components. School : holds/displays list of Students, which can be added, deleted. Student: holds/displaysinformation, such as name, address, which can be modified. Address: holds/displays address details, which can be modified.

Now, how can I do the state management here, say if user modifies address of one of the students, how can this reflect in school.

options I could think of: 1. use Redux, but say I want to create this School as component which will be used in another react app, would I be able use redux store at component level.

  1. pass the Student object to Student react component which will update this with information when modified.. mutating props.

Note: I'm new to react, excuse me something I wrote is incorrect or does not make sense.

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>

Thanks

1
If you plan to reuse a component in another app entirely, you would have to give that app its own state management. As for state management options, you have the context api, passing via props and redux. Redux is likely overkill for anything that isn't a very large and complex app. - Keno Clayton
I have updated the post with code snippent, where parent supplies the Student object as props to child and child updates whenever user changes.. But I per react guidelines this is not right approach(mutating in child).... may I know details in 'you have the context api, passing via props and redux'.. - TheCoder
I'll write an answer shortly - Keno Clayton

1 Answers

0
votes

Firstly, using local state in React is fine. The general rule is to lift state to a level where each component that relies on it, will have easy access to it. If the data within it only matters to that specific component, then keep it local.

Now, how can I do the state management here, say if user modifies address of one of the students, how can this reflect in school.

Based on your use case, it would be most effective to keep a list of students and their properties inside the <School /> component. That way you can pass it down as needed without having to worry.

I notice that you have state locally in each component, derived from its props. The more efficient thing to do is to simple create references to the props, and use those instead.

School Component (keep as is)

School should maintain the list of students, as well as all their sub-properties.

Student & Address Components

These two should always refer to their props, and call a parent method to update state.

Instead of

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

just do

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

in any function that you need to access those variables.

You should move your handleX type functions into School, and pass it via props.

You can also skip using too many props, and just use the Context API. For example...

//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;

Hope that clears it up, let me know if there's something you didn't understand.