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:
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:
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:
Advertisement
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