简体   繁体   中英

React: For Loop is not detecting Hook's State changes

Developing a React Aplication which primary functionality is to show each word of a sentence within a time interval previously set by the user (basically fast reading). I achieved this some days ago, but now im trying to implement a pause button (to pause the loop and get the actual word) and stuck.

I decided to add a Pause button, that calls a function that sets the pause state to true, and inside my loop, in the case this pause state is 'true', it breaks out of the loop. But the problem is, that this loop doesnt detect the change, I have a console.log in line 18, that is always logging false, although I had already clicked the pause button and changed the state to 'false' (And that is working because im also logging it in the useEffect and its loggin 'true')

So, my question here is, how can I solve this? Am I using react hooks wrong?

Code Sample:

const [time, setTime] = React.useState('500')
const [pause, setPause] = React.useState(false)
const go = async function(){
    let textarray = text.split(' ') //I split my text 
    for(let i = 0; i < textarray.length; i++){ 
        console.log(pause) //This keeps logging false although the change in pauseButton function
        if(pause){
            //Get the actual word => i
            break;
        }
        else{
            setValue(textarray[i]) //Show the actual word
            await timeout(time) //Wait the time
        }
    }
}
const pauseButton = function(){
    setPause(true)
}
function timeout(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

I think you might want to refactor this a bit. Consider setting up a useInterval() hook to run the counter. Then it will be easy to start and stop your counter.

Use useMemo hook for the the split textArray result, so it does not recalculate every refresh.

Then you can just get the value of textarray[i] anytime to place in your JSX.

This way you won't need any other async functions at all.

Basically, you don't understand javascript closures, so this is the reason why you're using hooks wrong in the context of the async function. You can't get state updates inside async function in a practical way. You should use useEffects or a custom hook ( Live demo to play ):

import React from "react";
import { useState } from "react";
import { useAsyncCallback } from "use-async-effect2";
import { CPromise } from "c-promise2";

function TestComponent(props) {
  const [text, setText] = useState("one two three four five");
  const [word, setWord] = useState("");

  const go = useAsyncCallback(
    function* (text, delay) {
      const words = text.split(/\s+/);
      for (const word of words) {
        setWord(word);
        yield CPromise.delay(delay);
      }
    },
    { states: true, cancelPrevios: true }
  );

  return (
    <div className="component">
      <div className="caption">useAsyncEffect demo</div>
      <input
        value={text}
        onChange={({ target }) => {
          setText(target.value);
        }}
      />
      <div>{go.error ? go.error.toString() : word}</div>
      {go.pending ? (
        go.paused ? (
          <button className="btn btn-warning" onClick={go.resume}>
            Resume
          </button>
        ) : (
          <button className="btn btn-warning" onClick={go.pause}>
            Pause
          </button>
        )
      ) : (
        <button className="btn btn-warning" onClick={() => go(text, 1000)}>
          Run
        </button>
      )}
      {go.pending && (
        <button className="btn btn-warning" onClick={go.cancel}>
          Cancel request
        </button>
      )}
    </div>
  );
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM