Невозможно получить доступ к счетчику после обновления - PullRequest
1 голос
/ 22 октября 2019

Я хочу начать interval, нажав на кнопку. Здесь interval запускается, но я не могу получить доступ к значению counter. потому что когда счетчик становится равным 5, интервал должен быть остановлен.

Вот пример:

let interval = null;
const stateReducer = (state, value) => value;

function App(props) {

  const [counter, setCounter] = useReducer(stateReducer, 0);

  const increment = () => {

    interval = setInterval(() => {
      setCounter(counter + 1);
      if (counter === 5) clearInterval(interval);
      console.log(counter);
    }, 1000);

  };

  return (
    <div>
      <p>{counter}</p>
      <button className="App" onClick={increment}>
        Increment
      </button>
    </div>
  );
}

Этот код можно запустить на codesandbox

Ответы [ 3 ]

1 голос
/ 22 октября 2019
  1. изменить const stateReducer = (state, value) => value; на const stateReducer = (state, value) => state+value;
  2. создать переменную let current_counter = 0; внешнюю функцию
  3. Изменить свою функцию приращения следующим образом
current_counter = counter;
const increment = () => {
   interval = setInterval(() => {
      setCounter(1);
      if (current_counter === 5) clearInterval(interval);
      console.log(current_counter);
    }, 1000);
};
  1. Done
1 голос
/ 22 октября 2019

У вашего кода есть несколько проблем.

Ваша декларация редуктора

const initialState = { value : 0 }

const reducer = (state, action) =>{
    if(action.type === 'INCREMENT') return { value : state.value + 1 }

    return state
}

Как вы настраиваете ваш редуктор

const [state, dispatch] = useReducer(reducer, initialState)

Как вы отправляете свое действие

intervals - это императивный код, вы не можете последовательно объявить interval внутри обработчика React, не беспокоясь о закрытии,Вы можете использовать click только для того, чтобы указать, что интервал должен начинаться, и обрабатывать весь императивный код внутри useEffect. Вот рабочий пример

const initialState = { value: 0 };
const reducer = (state, action) => {
  if (action.type === "INCREMENT")
    return {
      value: state.value + 1
    };
};
function App() {
  const [state, dispatch] = React.useReducer(reducer, initialState);
  const [clicked, setClicked] = React.useState(false);

  useEffect(() => {
    let interval = null;
    if (clicked) {
      interval = setInterval(() => {
        dispatch({ type: "INCREMENT" });
      }, 1000);
    }
    if (state.value > 4) clearInterval(interval);

    return () => clearInterval(interval);
  }, [clicked, state]);

  return <button onClick={() => setClicked(true)}>{state.value}</button>;
}

Edit holy-grass-22y29

Если вам интересно узнать о замыканиях и как реагировать на императивный код, взгляните на эту потрясающую статью от Дана Абрамова (самое подробное объяснение об эффектах там).

1 голос
/ 22 октября 2019
import React, { useReducer, useEffect, useState } from "react";
import ReactDOM from "react-dom";
import "./styles.css";

let interval = null;
let current_counter = 0;
const stateReducer = (state, value) => value;

function App(props) {
  const [status, setStatus] = useState(false);
  const [counter, setCounter] = useReducer(stateReducer, 0);
  useEffect(()=>{
    if(status){
      const interval = setInterval(() => {
          setCounter(counter + 1);
        }, 1000)
        return ()=>{
          clearInterval(interval)
        };
    }

  })
  const increment = () => {
    setStatus(!status)
  };

  return (
    <div>
      <p>{counter}</p>
      <button className="App" onClick={increment}>
        Increment
      </button>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
...