React js - Update a Parent component list/array through the Child component state

Asked

Viewed 631 times

0

Good evening, I’m new to React, I come from Java/C# programming and I’m having a hard time understanding things that might be simple in React js. My question is the following, I need to update a list contained in my parent Employee component through my child Form component when I click the save button, in which it performs the save() function by updating the child’s status. This same parent list I will use in the Table component. Follow the code:

PARENT COMPONENT

import React from "react";
import Main from "../templates/Main";
import Form from "./Form";
import Table from "./Table";

const headerProps = {
  title: "Employés",
  subtitle:
    "Inscription des employés: Inclusion, Listage, Édition, Suppression",
};

const employeeProps = {
  employeesList: [
    {
      lastName: "Pierini",
      name: "Alex",
      birthday: "1985-08-28",
      title: "Programmeur",
    },
    {
      lastName: "Ferreira",
      name: "Pedro",
      birthday: "1988-05-07",
      title: "Vendeur",
    },
    {
      lastName: "Pelela",
      name: "Tiago",
      birthday: "1982-03-14",
      title: "Infographiste",
    },
  ],
};

export default class Employee extends React.Component {
  constructor(props) {
    super(props);
    this.state = { ...employeeProps };
  }

  addEmployee(employeesList) {
    console.log("Ajoutée");

    this.setState({ employeesList: employeesList });
  }

  editEmployee() {
    console.log("Modifier");
  }

  deleteEmployee() {
    console.log("Supprimer");
  }

  render() {
    return (
      <Main {...headerProps}>
        <Form {...employeeProps} callbackParent={(employeesList) => this.addEmployee(employeesList)} />
        <Table
          {...employeeProps}
          editEmployee={() => this.editEmployee()}
          deleteEmployee={() => this.deleteEmployee()}
        />
      </Main>
    );
  }
}

CHILD COMPONENT

import React from "react";
import Button from "../Button";

const initialState = {
  employee: {
    lastName: "",
    name: "",
    birthday: "",
    title: "",
  },
};

export default class Form extends React.Component {
  constructor(props) {
    super(props);
    this.state = { ...initialState, ...props };
  }

  handleInputChange(event) {
    const employee = { ...this.state.employee };
    employee[event.target.name] = event.target.value;
    this.setState({ employee });
  }

  save() {          
    this.setState(
      function (state) {
        return {
          employeesList: [...state.employeesList, { ...state.employee }],
          employee: initialState.employee,
        };
      },
      (state) => this.props.callbackParent && this.props.callbackParent(state)
    );
  }

  clear() {
    this.setState({ employee: initialState.employee });
  }

  render() {    
    return (
      <div>
        <form>
          <label>Nom</label> <br />
          <input
            type="text"
            name="lastName"
            value={this.state.employee.lastName}
            onChange={(event) => this.handleInputChange(event)}
          />
          <br />
          <label>Prénom</label> <br />
          <input
            type="text"
            name="name"
            value={this.state.employee.name}
            onChange={(event) => this.handleInputChange(event)}
          />
          <br />
          <label>Date de Naissance</label> <br />
          <input
            type="date"
            name="birthday"
            value={this.state.employee.birthday}
            onChange={(event) => this.handleInputChange(event)}
          />
          <br />
          <label>Titre</label> <br />
          <select
            name="title"
            value={this.state.employee.title}
            onChange={(event) => this.handleInputChange(event)}
          >
            <option value="DEFAULT"></option>
            <option value="Infographiste">Infographiste</option>
            <option value="Programmeur">Programmeur</option>
            <option value="Vendeur">Vendeur</option>
          </select>
          <br />
        </form>
        <Button
          handleClick={() => this.save()}
          // handleClick={this.props.callbackParent({ ...this.state.employee })}
          label="Enregistrer"
        />
        <Button handleClick={() => this.clear()} label="Annuler" />
        <hr />
      </div>
    );
  }
}
  • did not work the past example?

2 answers

0


The code is so confused and the idea is very simple to implement that it is better for me to develop a minimal example than to tidy up your own, when working with states like this where components need to share I go straight to Context or Redux as in this example created by me, but, your code seems to do many things without need, if you want to change the state of the component pai need to pass a function of this component pai to change, in the case in question at the time of typing is already changed the state by the function onChangeItem, being a very basic example:

class Pai extends React.Component {
  constructor() {
    super();
    this.state = { 
      list: [
        {id: 1, name: 'item 1'},
        {id: 2, name: 'item 2'},
      ]
    };
    this.onChangeItem =
      this.onChangeItem.bind(this);
  }
  onChangeItem (item) {
    const newList = this.state.list.map(e => {
      if (e.id === item.id) return item;
      return e;
    });
    this.setState({list: newList});
  }
  render() {
    return (
      <div>
        <Filho list={this.state.list} onChangeItem={this.onChangeItem} />
      <div>{JSON.stringify(this.state.list)}</div>  
      </div>
    )
  }
};

class Filho extends React.Component {
  constructor(props){
    super(props);    
  }
  render() {
    const list = this.props.list;
    const onChangeItem = this.props.onChangeItem;
    return (
      <div>
        <ul>
          {list.map((l,i) => (
            <li>
              <input value={l.name} 
                onChange={e => onChangeItem({id: l.id, name:e.target.value})} /></li>
          ))}
        </ul>
      </div>
    )
  }
}

ReactDOM.render(<Pai/>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.9.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.9.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>

  • As I told you, I’m new to React and I come from another programming paradigm, which makes it harder for me to learn React. Which took me longer to study your answer, try to understand and apply in my code. I believe this is not a reason to deny my question, right? Or is it common to deny who takes a little longer to understand a reply?

  • @Alexforastierpierini first of all I have not denied your question this statement is unprecedented, what I understand is that in the network has someone at all times in questions of javascript negative, but, that person is not me.

0

After much study I managed to understand this logic of passing functions p/ the events of the children components. Follow the answer, I hope you help someone who went through the same difficulty as I did when he started learning React.

Component Pai

import React, { useState } from "react";
import Main from "../templates/Main";
import Form from "./Form";
import Table from "./Table";

const headerProps = {
  title: "Employés",
  subtitle:
    "Inscription des employés: Inclusion, Listage, Édition, Suppression",
};

const initialState = {
  employe: {
    lastName: "",
    name: "",
    birthday: "",
    title: "",
  },
  employesList: [],
};

export default (props) => {
  const [employe, setEmploye] = useState(initialState.employe);
  const [employesList, setEmployesList] = useState(initialState.employesList);
  const [employeChange, setEmployeChange] = useState(null);

  function handleInputChange(event) {
    setEmploye({ ...employe, [event.target.name]: event.target.value });
  }

  function saveEmploye() {
    const newList = employesList.concat(employe);
    setEmployesList(newList);
    setEmployeChange(null);
    setEmploye(initialState.employe);
  }

  function clearFormFields() {
    if (employeChange !== null) {
      const refreshList = employesList.concat(employeChange);
      setEmployesList(refreshList);
      setEmployeChange(null);
    }
    setEmploye(initialState.employe);
  }

  function editEmploye(lastName, name) {
    const employeToModify = employesList.filter(
      (employe) => employe.lastName === lastName && employe.name === name
    );
    setEmploye(employeToModify[0]);
    setEmployeChange(employeToModify[0]);
    deleteEmploye(lastName, name);
  }

  function deleteEmploye(lastName, name) {
    const newList = employesList.filter(
      (employe) => employe.lastName !== lastName && employe.name !== name
    );
    setEmployesList(newList);
  }

  return (
    <Main {...headerProps}>
      <Form
        employe={employe}
        onInputChange={handleInputChange}
        onSaveEmploye={saveEmploye}
        onClearFormFields={clearFormFields}
      />
      <Table
        employesList={employesList}
        onEditEmploye={editEmploye}
        onDeleteEmploye={deleteEmploye}
      />
    </Main>
  );
};

Component Filho

import React from "react";
import { makeStyles } from "@material-ui/core/styles";
import Button from "@material-ui/core/Button";
import SaveIcon from "@material-ui/icons/Save";
import ClearIcon from "@material-ui/icons/Clear";

const useStyles = makeStyles((theme) => ({
  button: {
    margin: theme.spacing(1),
  },
}));

export default (props) => {
  const classes = useStyles();

  return (
    <div>
      <form>
        <label>Nom</label> <br />
        <input
          type="text"
          name="lastName"
          value={props.employe.lastName}
          onChange={props.onInputChange}
          required
        />
        <br />
        <label>Prénom</label> <br />
        <input
          type="text"
          name="name"
          value={props.employe.name}
          onChange={props.onInputChange}
        />
        <br />
        <label>Date de Naissance</label> <br />
        <input
          type="date"
          name="birthday"
          value={props.employe.birthday}
          onChange={props.onInputChange}
        />
        <br />
        <label>Titre</label> <br />
        <select
          name="title"
          value={props.employe.title}
          onChange={props.onInputChange}
        >
          <option value="DEFAULT"></option>
          <option value="Infographiste">Infographiste</option>
          <option value="Programmeur">Programmeur</option>
          <option value="Vendeur">Vendeur</option>
        </select>
        <br />
      </form>
      <Button
        id="saveEmployeButton"
        variant="contained"
        size="small"
        startIcon={<SaveIcon />}
        className={classes.button}
        onClick={props.onSaveEmploye}
      >
        Enregistrer
      </Button>
      <Button
        id="clearFormFieldsButton"
        variant="contained"
        size="small"
        startIcon={<ClearIcon />}
        className={classes.button}
        onClick={props.onClearFormFields}
      >
        Annuler
      </Button>
      <hr />
    </div>
  );
};
  • You can add an answer to your question that is valid, even good, but, your quote is worrying if you married another problem should not be the answer to your question, here the problems we add in the question and finally we accept an answer if so want.

  • So, in case I accepted your answer as a solution, because I was able to actually solve when I understood this callback issue, but I believe that this other bug is due to having 2 callbacks, one in the Form component and the other for the Table component. In case then, for this new problem I should raise a new question beyond this?

Browser other questions tagged

You are not signed in. Login or sign up in order to post.