I am trying to disable a button onClick based on if the button is the "current player". I am able to successfully get the correct code in the console log from my PlayerButton
component, but I also get errors from my App.js
that .map is not a function
, meaning that my playerData
(which works on initial mount) becomes undefined somehow after the onClick (i also receive undefined
when trying to log playerData
in App.js
after the onClick).
Any ideas as to why my setPlayerData
function isn't passing the data back to the parent component? I am thinking I may need to either use async await or useEffect somewhere, but I'm not sure how to implement it in this case.
App.js
import React, { useState } from 'react';
import './app.css'
import PlayerButton from './components/PlayerButton';
function App() {
const [playerData, setPlayerData] = useState([
{
name: "Player 1",
currentPlayer: true,
position: 0
},
{
name: "Player 2",
currentPlayer: false,
position: 0
},
{
name: "Player 3",
currentPlayer: false,
position: 0
}
]);
return (
<div className="container">
{playerData.map((player, index) => (
<div key={player.name}>
<PlayerButton player={player} index={index} setPlayerData={setPlayerData} />
</div>
))}
</div>
);
}
export default App;
PlayerButton.js
import React from 'react';
const PlayerButton = ({ player: { name, currentPlayer }, index, setPlayerData }) => {
const handleButtonClick = () => {
setPlayerData(playerData => {
playerData.map(player => {
if (player.name === name) {
console.log({ ...player, currentPlayer: false })
return { ...player, currentPlayer: false }
}
return player
})
})
}
return (
<button disabled={!currentPlayer} type="button" id={index 1} className={`player${index 1}-btn`} onClick={handleButtonClick}>{name}</button>
)
}
export default PlayerButton;
I experimented with useEffect and async await because, I'm assuming, on update the parent component is trying to re-render before the data is successfully passed from the child. But if that were the case, wouldn't it just re-render the previous data?
Somehow, I'm not successfully updating the state in the child so the parent can use it.
CodePudding user response:
You've lost a return statement and returned undefined
instead
Should be:
setPlayerData(playerData => {
return playerData.map(player => {
if (player.name === name) {
console.log({ ...player, currentPlayer: false })
return { ...player, currentPlayer: false }
}
return player
})
})
CodePudding user response:
Ok I found a solution. I created a function in the parent component that uses the setPlayerData
function inside a callback setCurrentPlayer
, passed it to the child, and executed the callback inside the child.
App.js
const setCurrentPlayer = () => {
setPlayerData((playerData.map((player) => {
if (player.currentPlayer) {
return { ...player, currentPlayer: false}
}
return player
})))
}
PlayerButton.js
const handleButtonClick = () => {
setCurrentPlayer();
}