dimanche 22 août 2021

Checkbox not unchecking with React

I have an array of matches that I display as a list of rows in my React application. Each row has two athletes, where you can use the checkboxes to predict which one will win. Only one athlete per row can be checked, so I did something like the following to store who's checked, and who's not:

On initial mount I have an empty object in state called predictions. When a user starts interacting with the checkboxes, I use the index of each row as a key to store the id of one of the athletes. This is what it looks like in practice,

{
  0: '12864',
  3: '13333',
  5: '45362',
  ...etc
}

So you can get a better idea of what I'm working on, here is a screenshot.

enter image description here

In my React code, I map through the array of matches like so:

<ul>
{matches &&
    matches.map((match, i) => (
    <li
        key={match.fighter_a_name + match.fighter_b_name}
        className="text-center"
    >
        <Row>
        <Col md={5}>
            <h3>
            {match.fighter_a_name}
            </h3>

            <FormCheck
            type="checkbox"
            id={match.fighter_a_id}
            idx={i}
            onChange={handlePrediction}
            checked={
                parseInt(predictions[i]) === match.fighter_a_id ||
                false
            }
            />
        </Col>

        <Col md={2}>
            {/* <div>{match.weight}</div>
            <div>{match.result}</div>
            <div>{match.time}</div> */}
            <div>{match.rounds}</div>
        </Col>

        <Col md={5}>
            <h3>
            {match.fighter_b_name}
            </h3>

            <FormCheck
            id={match.fighter_b_id}
            idx={i}
            onChange={handlePrediction}
            checked={
                parseInt(predictions[i]) === match.fighter_b_id ||
                false
            }
            />
        </Col>
        </Row>
    </li>
    ))}
</ul>

Each click of the checkbox will execute a function named handlePrediction, that handles the logic of updating the predictions object. It basically checks to see if their is currently a key in the object with the same id value as the checkbox just clicked. If so, it will remove that key from the object, otherwise, it just adds that key to the object, or overrides the current id at that key.

I think I might have some logic issues in here, but for now, here it is.

const handlePrediction = (e) => {
    const id = e.target.id;
    const idx = e.target.getAttribute("idx");

    if (predictions[idx] === id) {
        setPredictions((prev) => {
        delete prev[idx];
        return prev;
        });
    } else {
        setPredictions((prev) => ({ ...prev, [idx]: id }));
    }
};

Now that I gave you the backstory, the problem I am having is that I can't uncheck the athletes in a specific scenario. If I check an athlete in one row, their id is successfully added to the object. Then, if I check the other athlete in the same row, the prior athlete becomes unchecked, and the one just clicked becomes checked, which is what I want.

However, if I check one athlete, and try to uncheck them, the checkbox stays checked permanently. On each click the handlePrediction function seems to handle everything correctly, and adds/removes the user from the object, but without any change in the checkbox.

I have no clue as to why the checkbox won't uncheck. I have created a slimmed down version of this code in a Codepen, which you can tinker around with, it has a full array of match data, so you can see what that looks like too.

P.S. I found something else wrong as well. If you use the codepen, do the following:

  1. Check Sean Strickland
  2. Check Cheyanne Buys
  3. Check Sean Strickland
  4. Check Cheyanne Buys
  5. Check Sean Strickland

After step number 5, you should see Cheyanne Buys become unchecked, which is not supposed to happen. I am definitely doing something wrong here.




Aucun commentaire:

Enregistrer un commentaire