Home > Net >  How to show the final state from UseState in my CookieClicker?
How to show the final state from UseState in my CookieClicker?

Time:01-31

i am a beginner with react and im working on a little clickergame. My problem is, that i want to use useState to automaticly increase the number (with setInterval) but i also want to increase the number with click on the button. The shown percentages are wild hopping because he shows me an early state and a later state at the same time.

function App() {
  const [findWorkCount, setfindWorkCount] = useState(0);

  setInterval(findWorkRunner, '500');

  function findWorkRunner() {
    setfindWorkCount(findWorkCount   1);

    if (findWorkCount >= 101) {
      setfindWorkCount(0);
    }
  }

  return (
    <div className="App">
      <button
        onClick={() => {
          setfindWorkCount(findWorkCount   11);
        }}
      >
        Find Job
      </button>
      <div className="bar">
        <div className="fillwork" style={{ width: `${findWorkCount}%` }}>
          <div className="counter">{findWorkCount}%</div>
        </div>
      </div>
    </div>
  );
}

CodePudding user response:

You can fix the issue by using the callback form of setfindWorkCount in the setInterval to ensure the state update happens after the render:

function App() {
  const [findWorkCount, setfindWorkCount] = useState(0);

  useEffect(() => {
    const intervalId = setInterval(() => {
      setfindWorkCount(c => c   1);
  
      if (findWorkCount >= 101) {
        setfindWorkCount(0);
      }
    }, 500);
    return () => clearInterval(intervalId);
  }, []);

  return (
    <div className="App">
      <button
        onClick={() => {
          setfindWorkCount(c => c   11);
        }}
      >
        Find Job
      </button>
      <div className="bar">
        <div className="fillwork" style={{ width: `${findWorkCount}%` }}>
          <div className="counter">{findWorkCount}%</div>
        </div>
      </div>
    </div>
  );
}

CodePudding user response:

Set up a setTimeout inside useEffect instead.

useEffect(() => {
  const id = setTimeout(() => {
    setfindWorkCount(findWorkCount   1);
    if (findWorkCount >= 100) setfindWorkCount(0);
  }, 500);
  return () => clearTimeout(id);
}, [findWorkCount]);

CodePudding user response:

When you use an interval in a function component you need to wrap in a useEffect block to ensure that it doesn't create an interval on each render. Since findWorkRunner is a dependency of the useEffect, you need to wrap it in useCallback. You should also use findWorkRunner for the button as well, so the same logic would apply to the button, and the interval updates.

Finally, use a function to update the state, because the updated state is computed using the previous state:

setfindWorkCount(count => 
  count   inc >= 101 ? 0 : count   inc
);

Example:

const { useState, useCallback, useEffect } = React;

function App() {
  const [findWorkCount, setfindWorkCount] = useState(0);

  const findWorkRunner = useCallback((inc = 1) => {
    setfindWorkCount(count => 
      count   inc >= 101 ? 0 : count   inc
    );
  }, []);
  
  useEffect(() => {
    const interval = setInterval(findWorkRunner, '500');
    
    return () => {
      clearInterval(interval);
    };
  }, [findWorkRunner]);
  

  return (
    <div className="App">
      <button
        onClick={() => {
          findWorkRunner(11);
        }}
      >
        Find Job
      </button>
      <div className="bar">
        <div className="fillwork" style={{ width: `${findWorkCount}%` }}>
          <div className="counter">{findWorkCount}%</div>
        </div>
      </div>
    </div>
  );
}

ReactDOM
  .createRoot(root)
  .render(<App />);
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>

<div id="root"></div>

  • Related