Automatically Changing State on a React App

Viewed 40

I'm looking to change the way I manage state in my app. Currently, I am using a mapped component which when selected, will set the index of the card and then use this index to colour the component background blue. This is great! And it works, however to change from e.g card 1 to 2, I need to tap on card 1 again to set index to 0, then select card 2. I do not know how to change the function so if selected outside the container, set index =0, then set index=1, per a conventional app.

I am managing as such:

  const [isSelected, setIsSelected] = useState("");
  

    function handleParamChange(e) {
    e.preventDefault();
    const param = e.target.name //name may be desc
    const value = e.target.value

    setParams(prevParams => {
      return { ...prevParams, [param]: value}
    })

  }

With a mapped component of:

                  {
                    jobs.length > 0 && 
                    jobs.map(
                      (job, index) => 
                      <JobCard 
                      key={job.id} 
                      job={job}
                      index={index + 1}
                      isSelected={isSelected}
                      setIsSelected={setIsSelected}
                      />)
                  }
const JobCard = ({ setIsSelected, isSelected, index, job }) => {

  const [open, setOpen] = useState(false)
  const [isActive, setIsActive] = useState(false);


  console.log(isSelected)

  return (
    
      <CardContainer>
        {/* BELOW WORKING */}
        {/* <CardPrimary onClick={() => setIsSelected(true)} className={isSelected ? "css-class-to-highlight-div" : undefined}> */}
        <CardPrimary 
          onClick={() => {
            if (!isSelected) {
              setIsSelected(index);
              setIsActive(true);
            } else if (isSelected === index) {
              setIsSelected("");
              setIsActive(false);
            }
          }}
          style={{
            backgroundColor: isActive ? "#0062ff" : "inherit",
            display: "flex",
            height: "90%",
            width: "95%",
            borderRadius: "10px",
            justifyContent: "center",
            flexDirection: "column",
            boxShadow: "0px 4px 10px rgba(0, 0, 0, 0.25)"
          }}
            >
          
          <CardHeader>
            <CardHeaderTopRow>
              <Typography variant = "cardheader1" color={isActive ? "white" : "inherit"}>
                {job.title}
              </Typography>
              <HeartDiv>
                <IconButton color={open ? "error" : "buttoncol"} sx={{ boxShadow: 3}} fontSize ="2px" size="small"
                            onClick={()=> setOpen(prevOpen => !prevOpen)}>
                  <FavoriteIcon fontSize="inherit"
                    />
                </IconButton>
              </HeartDiv>

            </CardHeaderTopRow>
            <Typography variant = "subtitle4" color={isActive ? "#d6d6d6" : "text.secondary"}>
              {job.company.display_name}
            </Typography>
          </CardHeader>
      </CardContainer>
  )
}

export default JobCard





This all works. And I have no issues, I just want to improve it. So How would you implement react hooks and a ref to automatically assign the state and change the isSelected index based on clicks?

1 Answers

If i understand correct, you only want one card to be selected and also the isActive inside the JobCard should be in sync with the isSelected you pass.

If so, you only need one state variable and to be safe, it should really be the job.id and not the index.

So, instead of const [isSelected, setIsSelected] = useState(""); you should rename it to something like const [selectedJob, setSelectedJob] = useState();

and also handle the toggle click here

const toggleJob = useCallback((jobId)=>{
   setSelectedJob( (currentJobId) => currentJobId === jobId ? null : jobId );
},[]);

Then

{
jobs.length > 0 && 
jobs.map(
  (job, index) => 
    <JobCard 
      key={job.id} 
      job={job}
      isSelected={job.id === selectedJob}
      toggleJob={toggleJob}
    />)
}

and finally

const JobCard = ({ toggleJob, isSelected, job }) => {

  const [open, setOpen] = useState(false)
  const handleCardClick = useCallback(()=>{
    toggleJob(job.id);
  },[toggleJob, job])

  return (
    
      <CardContainer>
        <CardPrimary 
          onClick={handleCardClick}
          style={{
            backgroundColor: isSelected ? "#0062ff" : "inherit",
            display: "flex",
            height: "90%",
            width: "95%",
            borderRadius: "10px",
            justifyContent: "center",
            flexDirection: "column",
            boxShadow: "0px 4px 10px rgba(0, 0, 0, 0.25)"
          }}
            >
          
          <CardHeader>
            <CardHeaderTopRow>
              <Typography variant = "cardheader1" color={isSelected ? "white" : "inherit"}>
                {job.title}
              </Typography>
              <HeartDiv>
                <IconButton color={open ? "error" : "buttoncol"} sx={{ boxShadow: 3}} fontSize ="2px" size="small"
                            onClick={()=> setOpen(prevOpen => !prevOpen)}>
                  <FavoriteIcon fontSize="inherit"
                    />
                </IconButton>
              </HeartDiv>

            </CardHeaderTopRow>
            <Typography variant = "subtitle4" color={isSelected ? "#d6d6d6" : "text.secondary"}>
              {job.company.display_name}
            </Typography>
          </CardHeader>
      </CardContainer>
  )
}

export default JobCard
Related