React - Stops Working After A Few Clicks

Hey,

This works… until it doesn’t. After a few clicks it stops working. Anyone know what’s going on? Thanks

import { useState } from 'react'


const Button = (props) => (
    <button onClick={props.handleClick}>Random Anecdote</button>
);

const App = () => {
  const [selected, setSelected] = useState(0);

  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 10 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.',
    'Programming without an extremely heavy use of console.log is same as if a doctor would refuse to use x-rays or blood tests when diagnosing patients.',
    'The only way to go fast, is to go well.'
  ]

  const randNum = Math.floor(Math.random() * anecdotes.length);

  const handleClick = () => {
    setSelected(randNum);
  }
  
  return (
    <div>
      <Button handleClick={handleClick} />
      {anecdotes[selected]}
    </div>
  )
}

export default App;

Your issue is here

You are calculating a random number only once when the component first renders.

But what you really want is to calculate a new random number each time the button is clicked.

You should move your randNum inside the handleclick function

 const handleClick = () => {
    const randNum = Math.floor(Math.random() * anecdotes.length);
    setSelected(randNum);
  };
1 Like

What was said is sort of correct, the problem is in part, where you are generating the index.

But all the code inside the component reruns on each render, top to bottom. This includes the random number generation.

The problem is the handler will at some point get stuck with an old value and when that happens the component no longer re-renders. So if your component generates the same index twice in a row the component will stop updating.

Changes to local variables do not trigger a re-render, so even though a new index is generated the state is stuck with the old value. The handler must update the state with a new value which is what will trigger a re-render.


I added some logs to the code.

const App = () => {
  console.log('Component rendered');
  const [selected, setSelected] = useState(0);

  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 10 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.',
    'Programming without an extremely heavy use of console.log is same as if a doctor would refuse to use x-rays or blood tests when diagnosing patients.',
    'The only way to go fast, is to go well.',
  ];

  const randNum = Math.floor(Math.random() * anecdotes.length);
  console.log('randNum in component', randNum);

  const handleClick = () => {
    console.log('randNum in handler', randNum);
    setSelected(randNum);
  };

  return (
    <div>
      {console.log('JSX ran')}
      <Button handleClick={handleClick} />
      {anecdotes[selected]}
    </div>
  );
};

Here is a log example:

Component rendered
randNum in component 5
JSX ran
randNum in handler 5
Component rendered
randNum in component 1
JSX ran
randNum in handler 1
Component rendered
randNum in component 0
JSX ran
randNum in handler 0
Component rendered
randNum in component 2
JSX ran
randNum in handler 2
Component rendered
randNum in component 7
JSX ran
randNum in handler 7
Component rendered
randNum in component 7
JSX ran
randNum in handler 7
Component rendered
randNum in component 6
JSX ran
(10) randNum in handler 7 // (10) is how may times the handler was clicked, as you can see it is now stuck

I might have gotten something wrong, explaining React can be tricky.

1 Like

That helps a lot to visualize it. Thanks!

Hey,

So it’s working now, kinda, but it’s janky. I think it has to do with the re-rendering, similar to what you were talking about. I vaguely remember seeing somewhere where if you try to do multiple things together (one right after the other) it doesn’t work right because the first one causes a re-render.

I think I’m trying to do too much (handleVote and handleSetMostVotes) at the same time. I originally had them in the same function, then separarted them and called one from the other. Then I tried calling them separately in the same event handler, and using separate event handlers, but haven’t found a fix for it so far. I made a CodeSandbox of it if you get a chance to check it out. Thanks

import { useState } from "react";

const Button = (props) => (
  <button onClick={props.handleClick}>Random Anecdote</button>
);

const Vote = (props) => <button onClick={props.handleVote}>Upvote!</button>;

const TopAnecdote = ({ anecdote, votes, mostVotesKey }) => (
  <>
    <p>Anecdote With The Most Votes:</p>
    <p>
      {anecdote}
      <br />
      {`has ${votes[mostVotesKey]} votes`}
    </p>
  </>
);

const App = () => {
  const [selected, setSelected] = useState(0);
  const [votes, setVotes] = useState({
    0: 0,
    1: 0,
    2: 0,
    3: 0,
    4: 0,
    5: 0,
    6: 0,
    7: 0
  });
  const [mostVotesKey, setMostVotesKey] = useState(0);

  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 10 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.",
    "Programming without an extremely heavy use of console.log is same as if a doctor would refuse to use x-rays or blood tests when diagnosing patients.",
    "The only way to go fast, is to go well."
  ];

  const handleClick = () => {
    const randNum = Math.floor(Math.random() * anecdotes.length);
    setSelected(randNum);
  };

  const handleVote = () => {
    setVotes((prev) => ({
      ...prev,
      [selected]: votes[selected] + 1
    }));
    handleSetMostVotes();
  };

  const handleSetMostVotes = () => {
    let most = 0;
    let greatestKey;
    for (let key in votes) {
      if (votes[key] > most) {
        most = votes[key];
        greatestKey = key;
      }
    }
    setMostVotesKey(greatestKey);
  };

  return (
    <div>
      <Button handleClick={handleClick} />
      <p>{anecdotes[selected]}</p>
      <Vote handleVote={handleVote} handleSetMostVotes={handleSetMostVotes} />
      {" " + votes[selected]}
      <TopAnecdote
        anecdote={anecdotes[mostVotesKey]}
        mostVotesKey={mostVotesKey}
        votes={votes}
      />
    </div>
  );
};

export default App;

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.