How to immediatelly update Parent state inside Child components

Tags: , , ,



I’m trying to immediatelly update a parent components’ state so I can properly use it in a Child (nested) component. I will try to give as many details as possible so you an understand.

So basically I have a parent component (App.js):

import React, { useState } from "react";
import Child from "./Child";
import Modal from "./Modal";
import "./styles.css";

export default function App() {
  const [options, setOptions] = useState([
    { Player: "Kevin Durant", Team: "Brooklyn Nets" },
    { Player: "LeBron James", Team: "Los Angeles Lakers" },
    { Player: "Michael Jordan", Team: "Chicago Bulls" }
  ]);
  const [selectedOption, setSelectedOption] = useState({});
  const [modalContent, setModalContent] = useState(null);
  const [show, setShowModal] = useState(false);

  const showModal = () => {
    setShowModal(true);
  };

  const hideModal = () => {
    setShowModal(false);
  };
  return (
    <div className="App">
      <div
        className="row"
        style={{
          justifyContent: "center",
          width: "100%",
          margin: "40px 0px 0px 0px"
        }}
      >
        <div
          className="table-cell other"
          onClick={() => {
            setModalContent(() => (
              <Child
                options={options}
                selectedOption={selectedOption}
                setSelectedOption={setSelectedOption}
              />
            ));
            showModal();
          }}
        >
          <div className="table-cell-text">Click to access Child component</div>
        </div>
      </div>
      <Modal
        show={show}
        modalClosed={hideModal}
        width={"40%"}
        title={"Choose a Player"}
      >
        {modalContent}
      </Modal>
    </div>
  );
}

App.js (parent component) visual:

enter image description here

This component has an array of objects (options) that is sent to the Child component as props.

Array of objects I mentioned:

  [
    { Player: "Kevin Durant", Team: "Brooklyn Nets" },
    { Player: "LeBron James", Team: "Los Angeles Lakers" },
    { Player: "Michael Jordan", Team: "Chicago Bulls" }
  ]

Basically the Child component has a Modal component and a select input, which will show the options.

Child.js:

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

const Child = (props) => {
  return (
    <div style={{ position: "relative", margin: "0 auto", width: "10em" }}>
      <div className="input-group col">
        <select
          className="form-control"
          onChange={(e) => {
            let foundOption = props.options.find(
              (options) => options.Player === e.target.value
            );
            props.setSelectedOption(foundOption);
          }}
        >
          <option value="">Select a Player...</option>
          {props.options.map((option) => (
            <option key={option.Player} value={option.Player}>
              {option.Player}
            </option>
          ))}
        </select>
      </div>
      <Team selectedOption={props.selectedOption} />
    </div>
  );
};

export default Child;

Child.js visual:

enter image description here

The Child component also has a Team component. Basically the Team component receive the selectedOption as props and shows a div with the selectedOption.Team value.

Team.js:

import React from "react";

const Team = (props) => {
  console.log(props.selectedOption);
  return (
    <div style={{ marginTop: "30px" }}>
      Team:{" "}
      {props.selectedOption !== undefined ? props.selectedOption.Team : ""}
    </div>
  );
};

export default Team;

The thing is, if I select an option, the Team component doesn’t receive the updated selectedOption immediatelly. So, if I want to show the players’ team, I need to select an option, close the modal and reopen it again.

I also recorded a video to show the issue: https://youtu.be/3P1tURgxvTQ

I would like to know how can I make it work properly! If you guys don’t understand my question, please let me know and I will try to improve it. Thank you all!

My CodeSandbox:

Edit optimistic-browser-870oe

Answer

This would be your App.js file:

import React, { useState } from "react";
import Child from "./Child";
import Modal from "./Modal";
import "./styles.css";

export default function App() {
  const [options, setOptions] = useState([
    { Player: "Kevin Durant", Team: "Brooklyn Nets" },
    { Player: "LeBron James", Team: "Los Angeles Lakers" },
    { Player: "Michael Jordan", Team: "Chicago Bulls" },
  ]);
  const [selectedOption, setSelectedOption] = useState(); // <--- remove the {} from here because your checking props.selectedOption !== undefined in Team Comp
  const [modalContent, setModalContent] = useState(false); // <--- the content state is now just a boolean
  const [show, setShowModal] = useState(false);

  const showModal = () => {
    setShowModal(true);
  };

  const hideModal = () => {
    setShowModal(false);
  };
  return (
    <div className="App">
      <div
        className="row"
        style={{
          justifyContent: "center",
          width: "100%",
          margin: "40px 0px 0px 0px",
        }}
      >
        <div
          className="table-cell other"
          onClick={() => {
            setModalContent(true);
            showModal();
          }}
        >
          <div className="table-cell-text">Click to access Child component</div>
        </div>
      </div>
      <Modal
        show={show}
        modalClosed={hideModal}
        width={"40%"}
        title={"Choose a Player"}
      >
        {modalContent && (
          <Child
            options={options}
            selectedOption={selectedOption}
            setSelectedOption={setSelectedOption}
          />
        )}
      </Modal>
    </div>
  );
}

This would be the Child.js

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

const Child = (props) => {
  return (
    <div style={{ position: "relative", margin: "0 auto", width: "10em" }}>
      <div className="input-group col">
        <select
          className="form-control"
          onChange={(e) => {
            let foundOption = props.options.find(
              (options) => options.Player === e.target.value
            );
            props.setSelectedOption(foundOption);
          }}
        >
          <option value="">Select a Player...</option>
          {props.options.map((option) => (
            <option key={option.Player} value={option.Player}>
              {option.Player}
            </option>
          ))}
        </select>
      </div>
      <Team selectedOption={props.selectedOption} />
    </div>
  );
};

export default Child;

and this would be the Team.js

import React from "react";

const Team = (props) => {
  console.log(props.selectedOption);
  return (
    <div style={{ marginTop: "30px" }}>
      Team:{" "}
      {props.selectedOption && props.selectedOption.Team || ""}
    </div>
  );
};

export default Team;

Although I’m not quite sure why you keep the options as a state or why you need the selectedOption in App.js, it makes sense to put it in Child.js



Source: stackoverflow