I have a state holding an array of objects (players) that I retrive from my API and then I'm rendering the users avatar on screen using the map function
{players.map(player => {
return (
<TouchableOpacity
key={player._id}
style={selectedPlayers.inclues(player) ? styles.userAvatarContainerSelected : styles.userAvatarContainer}
onPress={() => handlePlayerAvatarClick(player)}
>
<Image
source={{
uri: player.avatar_url
}}
style={styles.userAvatar}
/>
</TouchableOpacity>
);
})}
My problem here is that when I tap the button I want to update a state of a state called selectedPlayers so I can update the style accordingly and then retrive the state back to the API.
I'm inicializing the state as const [playersAvatars, setPlayersAvatars] = useState([]);
And the handlePlayerAvatarClick function is:
function handlePlayerAvatarClick(newPlayer) {
if (newPlayer in selectedPlayers) {
const arrayWithRemovedPlayer = selectedPlayers.filter(player => player._id.equals(newPlayer._id));
setSelectedPlayers(arrayWithRemovedPlayer);
}
setSelectedPlayers(...selectedPlayers, newPlayer);
}
I'm getting an error saying that selectedPlayers.includes() is not a function I don't get the problem, if I run Array.isArray(selectedPlayers)
it returns true
so, in theory, I could use the array functions like includes().
Any idea on what the issue is?
Thank you for your time!
You did not pass the correct value to setSelectedPlayers and should have done setSelectedPlayers([...selectedPlayers, newPlayer])
.
Here is an optimised version where I use the callback version of state setter setSomeState(oldvalue=>newValue
:
//using React.memo will make Player a pure
// component and won't re render if props didn't change
const Player = React.memo(function Player({
player,
isSelected,
handlePlayerAvatarClick,
}) {
const r = React.useRef(0);
r.current++;
return (
<li
onClick={handlePlayerAvatarClick(player)}
style={{ cursor: 'pointer' }}
>
rendered:{r.current} times, name: {player.name}, is
selected:
{isSelected.toString()}
</li>
);
});
const players = [
{ id: 1, name: '1', completed: false },
{ id: 2, name: '2', completed: false },
];
function App() {
const [
selectedPlayers,
setSelectedPlayers,
] = React.useState([]);
//use callback so the handler never changes
const handlePlayerAvatarClick = React.useCallback(
(player) => () =>
setSelectedPlayers((selectedPlayers) =>
selectedPlayers.includes(player)
? selectedPlayers.filter((p) => p !== player)
: [...selectedPlayers, player]
),
[]
);
return (
<ul>
{players.map((player) => (
<Player //player is pure and won't re render if nothing changed
key={player.id}
player={player}
isSelected={selectedPlayers.includes(player)}
handlePlayerAvatarClick={handlePlayerAvatarClick}
/>
))}
</ul>
);
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>