Home > Net >  How to send data from a child component to parent component in React
How to send data from a child component to parent component in React

Time:10-06

Having the following code structe, App is the parent, Inputs and List are children, for the moment we focus only on first two.

App.js:

import React from 'react';
import './App.css';
import Inputs from './components/Inputs';
import List from './components/List';
import { useState } from 'react';

function App() {
  const [person, setPerson] = useState({ name: '', email: '' });
  const people = [];

  const callback = (e) => {
    people.push(e);
  };

  console.log('people: ', people);
  return (
    <div>
      <Inputs callback={callback} person={person} setPerson={setPerson} />
      <List />
    </div>
  );
}

export default App;

Inputs.js:

const Inputs = ({ person, setPerson, callback }) => {
  const updateName = (e) => {
    setPerson({ ...person, name: e.target.value });
  };

  const updateEmail = (e) => {
    setPerson({ ...person, email: e.target.value });
  };

  console.log('person: ', person);
  return (
    <div>
      <label>name:</label>
      <input onChange={updateName}></input>
      <label>email:</label>
      <input onChange={updateEmail}></input>
      <button onClick={callback(people)}>add person</button>
    </div>
  );
};

export default Inputs;

When I introduce value into the inputs, the person object is getting updated, the log is showing that, but when I click on the button, the callback doesn't work, it doesn't update the people list in parent component.

How can I fix that?

CodePudding user response:

There's many things wrong in your code. Firstly, you can't store values as plain variables in the body of a functional component. Every time the component re-renders, that value is re-initialized. So first you have to put people into state:

const [people, setPeople] = useState([]);

Secondly, when updating it you can't push into it, cos you can't mutate state, so when adding a person to that array you have to use the state setting function:

const addPerson = (person) => {
  setPeople(people => [...people, person]);
};

Thirdly, in your child the on click parameter should be a function, not a function call. Also you reference the variable people in the child but you don't actually pass it in as a prop so the child has no idea what that variable refers to. But with the people now in state and you having an add person function, you can just pass that in as a prop and then in the child have onClick={addPerson}

CodePudding user response:

Issues

There are 2 issues:

  1. The callback is immediately invoked in the child.
  2. people is redeclared each time the parent component renders. people should be part of React state.

Solution

Parent

function App() {
  const [person, setPerson] = useState({ name: '', email: '' });
  const [people, setPeople] = useState([]);

  const callback = () => {
    setPeople(people => people.concat(person)); // <-- append current person value
    setPerson({ name: '', email: '' }); // <-- reset person state
  };

  return (
    <div>
      <Inputs callback={callback} setPerson={setPerson} />
      <List />
    </div>
  );
}

Child

const Inputs = ({ setPerson, callback }) => {
  const updateName = (e) => {
    setPerson(person => ({
      ...person,
      name: e.target.value
     }));
  };

  const updateEmail = (e) => {
    setPerson(person => ({
      ...person,
      email: e.target.value
    }));
  };

  return (
    <div>
      <label>name:</label>
      <input onChange={updateName}></input>
      <label>email:</label>
      <input onChange={updateEmail}></input>
      <button onClick={callback}>add person</button> // <-- don't immediately invoke
    </div>
  );
};

CodePudding user response:

I believe what you need to do is not call the function directly. By calling it directly the function is immediately invoked and the return value is lost. callback(people) is equivalent to a value. So by passing event => callback(people) we preserve the return value until the button is pressed.

const Inputs = ({ person, setPerson, callback }) => {
  const updateName = (e) => {
    setPerson({ ...person, name: e.target.value });
  };

  const updateEmail = (e) => {
    setPerson({ ...person, email: e.target.value });
  };

  console.log('person: ', person);
  return (
    <div>
      <label>name:</label>
      <input onChange={updateName}></input>
      <label>email:</label>
      <input onChange={updateEmail}></input>
      <button onClick={event => callback(people)}>add person</button>
    </div>
  );
};

export default Inputs;
  • Related