React - setState in child through props

Viewed 133

I'm relatively new to React and am trying to set the state of an object inside the child component(SportTile) for the onclick event. I'm comfortable updating the state of a single variable by using individual useState() hooks but, that is a cumbersome task when you have a lot of variables. So I used an object named : isClicked which stores a boolean value for various sports on whether they are selected by the user or not.

The functionality that I'm looking for is, whenever a SportTile gets clicked on, its isClicked["sportname"] value is set to true and the rest are set to false. Only one sport can be true at once. Upon console logging the isClicked object in the onclick() function, I got the desired values but they weren't updated in the Parent component's(Booking) h1 tag

import React from 'react';
import SportTile from '../SportTile';
import './booking.css';
import { useState } from 'react';
import SportsSoccerRoundedIcon from '@mui/icons-material/SportsSoccerRounded';
import SportsBasketballRoundedIcon from '@mui/icons-material/SportsBasketballRounded';
import SportsVolleyballIcon from '@mui/icons-material/SportsVolleyball';
import SportsTennisIcon from '@mui/icons-material/SportsTennis';

const Booking = () => {
  const [isClicked, setIsClicked] = useState({
    Football: false,
    Basketball: false,
    Volleyball: false,
    Tennis: false,
  });

  return (
    <div className='booking'>
      <div className='booking__body'>
        <div className='booking__left'>
          <h1>SELECT SPORT</h1>
          <SportTile
            sportname={'Football'}
            icon={<SportsSoccerRoundedIcon />}
            clicked={setIsClicked}
            isClicked={isClicked}
            // test={setTestclicked}
            // istestClicked={istestClicked}
          />
          <SportTile
            sportname={'Basketball'}
            icon={<SportsBasketballRoundedIcon />}
            clicked={setIsClicked}
            isClicked={isClicked}
          />
          <SportTile
            sportname={'Volleyball'}
            icon={<SportsVolleyballIcon />}
            clicked={setIsClicked}
            isClicked={isClicked}
          />
          <SportTile
            sportname={'Tennis'}
            icon={<SportsTennisIcon />}
            clicked={setIsClicked}
            isClicked={isClicked}
          />

          <h1>Football : {isClicked.Football.toString()} </h1>
          <h1>Basketball : {isClicked.Basketball.toString()} </h1>
          <h1>Volleyball : {isClicked.Volleyball.toString()} </h1>
          <h1>Tennis : {isClicked.Tennis.toString()} </h1>
        </div>
        <div className='booking__right'>Right Side</div>
      </div>
    </div>
  );
};

export default Booking;

import { Button } from '@mui/material';
import React from 'react';
import './sportTile.css';

const SportTile = ({
  sportname,
  icon,
  clicked,
  isClicked,     
}) => {
  function onclick() {        
    Object.keys(isClicked).map((key) => {
      if (key == sportname) {
        isClicked[key] = true;
      } else {
        isClicked[key] = false;
      }
    });      
    clicked(isClicked);
    console.log(isClicked);
  }

  return (
    <Button className='sportname__button' onClick={onclick}>
      <div className='sportname__buttonColumn'>
        {icon}
        {sportname}
      </div>
    </Button>
  );
};

export default SportTile;

Maybe I'm missing the obvious but would appreciate anyone who could point me in the right direction

1 Answers

You should never pass the original setState method. create a new method in the Booking component:

const setIsClickedWrapper = (sportKey) = {
   setIsClicked((isClicked) => Object.keys(isClicked).map((key) => sportKey === key)
} 

and in the SportTile component just call:

const onclick = () => { setIsClickedWrapper(sportname) }     

but I think it will be better if isClicked will be just a string of the current clicked sport key and then it's enough:

const setIsClickedWrapper = (sportKey) = {
   setIsClicked(sportKey)
} 
Related