0

Aim: To update the index of a given array by 1 when a click event occurs on vote button

My code: I have a single app component, where I have 2 state objects and 1 array

const App = () => {
    const anecdotes = [
        'If it hurts, do it more often',
        'Adding manpower to a late software project makes it later!',
        'The first 90 percent of the code accounts for the first 90 percent of the development time...The remaining 10 percent of the code accounts for the other 90 percent of the development time.',
        'Any fool can write code that a computer can understand. Good programmers write code that humans can understand.',
        'Premature optimization is the root of all evil.',
        'Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.'
    ]

    // to select anecdotes at random
    const [selected, setSelected] = useState(0);

    // elements in anecdotes
    let n = anecdotes.length;
    const [points, setPoints] = useState(() => Array(n).fill(0));

    // to use with setSelected 
    const generateRandomNumber = (min, max) => {
        min = Math.ceil(min);
        max = Math.floor(max);
        return Math.floor(Math.random() * (max - min) + min); 
    }

    // eventHandler
    const handleClickNext = () => {
        return setSelected(generateRandomNumber(0, n))
    }

    const handleClickVote = () => {
        const copy = [...points]
        console.log(copy);
        console.log(selected);
        console.log(copy[selected]);
        console.log(points);
        return setPoints([
            copy[selected] += 1
        ])
    }

    return (
        <>
            <h1>anecdoteZ</h1>
           // code...
        </>
    )
}

export default App;

Objective: The app displays random quotes from the anecdotes array along with that anecdotes vote count and ability to vote for the anecdote.

Problem: I think the problem is my handleClickVote function

 const handleClickVote = () => {
        const copy = [...points]
        console.log(copy);
        console.log(selected);
        console.log(copy[selected]);
        console.log(points);
        return setPoints([
            copy[selected] += 1
        ])
    }

The code works okay with the first anecdote that is displayed, but when I click on next quote button, and vote on the other quotes, it returns NaN in console since I'm logging the result. Also I think I'm using setPoints to set teh state of my array incorrectly. I dont want to mutate the state directly, I want to create a copy and mutate that instead to update the state of my points array.

NB This is what my app component returns

<>
            <h1>anecdoteZ</h1>
            <span anecdotes={anecdotes} selected={selected}>
                {anecdotes[selected]}
            </span>
            <br />
            <br />
            <span points={points} selected={selected}>has {points[selected]} votes</span>
            <br />
            <button onClick={handleClickVote}>vote</button>
            <button onClick={handleClickNext}>
                next anecdote
            </button>
            <br />
            <br />
            <span>Top voted anecdote</span>
        </>

3 Answers 3

1

You assumed correctly, the problem is the handleClickVote function. As it stands, on the first vote, you set the state to an array with exactly one element.

return setPoints([       // <<-- new array starts here
    copy[selected] += 1  // <<-- it's only element, will be the the result of
                         //      the increment of whatever is in copy[selected] plus 1
]);

What you want is to update whatever is in copy, and assign copy as the new state. E.g.:

copy[selected]++;
return setPoints(copy);

Or (with borrowing from https://stackoverflow.com/a/45673826), simply:

const handleClickVote = () => {
    return setPoints(current => Object.assign([], current, {
      [selected]: current[selected] + 1
    }));
}
2
  • Yoshi, the last method is a bit complicated, will read more from the post you have mentioned. It worked from, is this according to the best practice that we should not modify state directly? When i create a copy and then use setPoints(copy) this is best practice right? Commented Apr 16, 2021 at 11:17
  • Yeah it looks a bit daunting, though it actually also creates a copy of the state. The example uses the callback version of the updater (setPoints), which gives the current state. With Object.assign([], ...), a new array is created, merged with current, and lastly merged with a new object whose sole property is the index you wanted to update.
    – Yoshi
    Commented Apr 16, 2021 at 11:26
1

Method handleClickVote is setting the value to an array of length 1. I can't test it but I'd suggest to change it for:

copy[selected] += 1;
return setPoints(copy);
0

As a start, when you pass the length of anecdotes into generating a random number it should be anecdotes.length - 1.

also no need to return setPoints - that might be your issue

2
  • The maximum is excluded in my generateRandomNumber function, so I think it'll work Commented Apr 16, 2021 at 10:30
  • remove the return on setPoints and see if that works
    – Jack
    Commented Apr 16, 2021 at 10:32

Not the answer you're looking for? Browse other questions tagged or ask your own question.