I'm new to React and basically I'm trying to update a parent App.js
components' state and its child components (Team.js
and Player.js
) props at once. Currently only the parent components' state is being updated. I will try to explain it better with a step by step.
Here I have a parent component App.js
export default function App() {
const teams = [
{
Name: "Chicago Bulls",
Players: ["Michael Jordan", "Dennis Rodman", "Scottie Pippen"],
Championships: 6
},
{
Name: "Golden State Warriors",
Players: ["Stephen Curry", "Klay Thompson", "Draymond Green"],
Championships: 5
},
{
Name: "Los Angeles Lakers",
Players: ["Kobe Bryant", "LeBron James", "Magic Johnson"],
Championships: 17
}
];
const [selectedTeam, setSelectedTeam] = useState({});
const players = [
{ Name: "LeBron James", MVPs: 4 },
{ Name: "Michael Jordan", MVPs: 5 },
{ Name: "Stephen Curry", MVPs: "2" }
];
const [selectedPlayer, setSelectedPlayer] = useState({});
const [modalContent, setModalContent] = useState(false);
const clickedComponent = useRef(null);
const [show, setShowModal] = useState(false);
const showModal = () => {
setShowModal(true);
};
const hideModal = () => {
setShowModal(false);
};
const handleModalContent = (clicked) => {
switch (clicked) {
case "Team":
clickedComponent.current = (
<Team
teams={teams}
selectedTeam={selectedTeam}
setSelectedTeam={setSelectedTeam}
/>
);
break;
case "Player":
clickedComponent.current = (
<Player
players={players}
selectedPlayer={selectedPlayer}
setSelectedPlayer={setSelectedPlayer}
/>
);
break;
default:
clickedComponent.current = null;
break;
}
};
return (
<div className="App" style={{ justifyContent: "space-evenly" }}>
<div
style={{
justifyContent: "center",
width: "100%",
display: "flex",
flexWrap: "wrap",
margin: "40px 0px 0px 0px"
}}
>
<div
className="table-cell"
onClick={() => {
handleModalContent("Team");
setModalContent(true);
showModal();
}}
>
<div className="table-cell-text">Click to access Team component</div>
</div>
<div
className="table-cell"
onClick={() => {
handleModalContent("Player");
setModalContent(true);
showModal();
}}
>
<div className="table-cell-text">
Click to access Player component
</div>
</div>
</div>
<h3 style={{ marginTop: "30px" }}>
The last selected team was: {selectedTeam.Name}
<br />
The last selected player was: {selectedPlayer.Name}
</h3>
<Modal show={show} modalClosed={hideModal}>
{(modalContent && clickedComponent.current) || null}
</Modal>
</div>
);
}
This component has two arrays of objects (teams
and players
) that is sent to Team
and Player
components, respectively, as props. Team
also receives selectedTeam
and setSelectedTeam
as props. Player
receives selectedPlayer
and setSelectedPlayer
. Both components have a Modal
component and a select input. In the Team
component, the user will select a team and them it will be displayed the selected teams' players, while in the Player
component a player will be select and them it will be displayed the amount of MVP of the selected player.
Team.js
const Team = (props) => {
return (
<div style={{ position: "relative", margin: "0 auto", width: "10em" }}>
<h3>Select a team</h3>
<div className="input-group col">
<select
onChange={(e) => {
if (e === "") props.setSelectedTeam({});
else {
let foundTeam = props.teams.find(
(team) => team.Name === e.target.value
);
props.setSelectedTeam(foundTeam);
}
}}
>
<option value="">Select a team...</option>
{props.teams.map((team) => (
<option key={team.Name} value={team.Name}>
{team.Name}
</option>
))}
</select>
</div>
{Object.keys(props.selectedTeam).length > 0 ? (
<div>
<h3>{props.selectedTeam.Name} players: </h3>
<br />
{props.selectedTeam.Players.map((player, index) => (
<div key={index}>{player}</div>
))}
</div>
) : null}
</div>
);
};
export default Team;
Player.js
const Player = (props) => {
return (
<div style={{ position: "relative", margin: "0 auto", width: "10em" }}>
<h3>Select a player</h3>
<div className="input-group col">
<select
onChange={(e) => {
if (e === "") props.setSelectedPlayer({});
else {
let foundPlayer = props.players.find(
(player) => player.Name === e.target.value
);
props.setSelectedPlayer(foundPlayer);
}
}}
>
<option value="">Select a player...</option>
{props.players.map((player) => (
<option key={player.Name} value={player.Name}>
{player.Name}
</option>
))}
</select>
</div>
{Object.keys(props.selectedPlayer).length > 0 ? (
<div>
<h3>
{props.selectedPlayer.Name} MVPs: {props.selectedPlayer.MVPs}
</h3>
</div>
) : null}
</div>
);
};
export default Player;
So my problem is, if I select an option in the child components, they don't receive the updated selected option (I mean selectedTeam
for Team
component and selectedPlayer
for Player
component) immediatelly but in the father component App
I have them updated. So, if I want them to get updated, I need to select an option, close the modal and reopen them again.
For example, here I have App.js
visual:
If I open Team.js
and select a team, I have selectedTeam
updated in App.js
but not in Team.js
:
So, if I close the modal and reopen Team
component again, then I have props.selectedTeam
updated. So I have the following:
I have the same problem with Player
component, but in this case regarding props.selectedPlayer
How can I make it work properly, I mean, how can I have props.selectedTeam
and props.selectedPlayer
updated at once in App
such as in Team
and Player
, respectively? Thank you!
CodeSandbox
https://codesandbox.io/s/young-sun-gs117?file=/src/Team.js:51-1127