反应挂钩useState的setter内部函数不起作用

时间:2019-10-07 20:18:41

标签: reactjs react-hooks

我正在尝试对我正在处理的项目具有的倒计时组件进行重构。

当我完成逻辑的迁移时,计数器的值不起作用。我决定在codeandbox中从零开始,因此我想最简单的实现,并得出了这样的结论:

import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";

import "./styles.css";

function App() {
  const [counter, setCounter] = useState(60);

  useEffect(() => {
    const interval = setInterval(() => setCounter(counter - 1), 1000);

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

  return (
    <div className="App">
      <h1>Hello CodeSandbox {counter}</h1>
      <h2>Start editing to see some magic happen!</h2>
    </div>
  );
}

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

在这里发生的是,counter的值在间隔的第一次运行后保持在59。

代码和框:https://codesandbox.io/embed/flamboyant-moon-ogyqr

问题的第二次迭代

谢谢罗斯的回答,但是真正的问题发生在我将倒计时链接到处理程序时:

import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";

import "./styles.css";

function App() {
  const [counter, setCounter] = useState(60);
  const [countdownInterval, setCountdownInterval] = useState(null);


  const startCountdown = () => {
    setCountdownInterval(setInterval(() => setCounter(counter - 1), 1000));
  };

  useEffect(() => {
    return () => clearInterval(countdownInterval);
  });

  return (
    <div className="App" onClick={startCountdown}>
      <h1>Hello CodeSandbox {counter}</h1>
      <h2>Start editing to see some magic happen!</h2>
    </div>
  );
}

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

2 个答案:

答案 0 :(得分:1)

counter函数的第二个参数(数组)内添加useEffect变量。当您传递一个空数组时,它将仅在初始渲染时更新一次state。当您发出HTTP请求或类似内容时,通常使用空数组。 (已编辑第二次迭代)

import React, { useEffect, useState } from "react";
import ReactDOM from "react-dom";

import "./styles.css";

function App() {
  const [counter, setCounter] = useState(5);
  const [counterId, setCounterId] = useState(null);

  useEffect(() => {
    return () => clearInterval(counterId);
  }, []);

  const handleClick = () => {

    /* 
     * I'd take startCountdown and make
     * it's own component/hook out of it, 
     * so it can be easily reused and expanded.
     */
    const startCountdown = setInterval(() => {
      return setCounter((tick) => {
        if (tick === 0) {
          clearInterval(counterId);
          setCounter(0);
          return setCounterId(null);
        };

        return tick - 1;
      });
    }, 1000)

    setCounterId(startCountdown);
  };

  return (
    <div className="App" onClick={handleClick}>
      <h1>Hello CodeSandbox {counter}</h1>
      <h2>Start editing to see some magic happen!</h2>
    </div>
  );
}

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

有关此实现的更多信息,请在https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects阅读有关React Hooks和跳过效果的信息。

答案 1 :(得分:1)

您可以使用useState返回的函数的Functional Updates版本来基于先前状态计算新状态。

您更新的代码如下:

import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";

import "./styles.css";

function App() {
  const [counter, setCounter] = useState(60);

  useEffect(() => {
    const interval = setInterval(() => {setCounter(counter => counter - 1);}, 1000);

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

  return (
    <div className="App">
      <h1>Hello CodeSandbox {counter}</h1>
      <h2>Start editing to see some magic happen!</h2>
    </div>
  );
}

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

更新编辑 这是一个通过点击处理程序开始倒计时的版本:

import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";

import "./styles.css";

function App() {
  const [counter, setCounter] = useState(60);
  const [countdownInterval, setCountdownInterval] = useState(null);


  const startCountdown = () => {
    setCountdownInterval(setInterval(() => setCounter(counter => counter - 1), 1000));
  };

  useEffect(() => {
    return () => clearInterval(countdownInterval);
  }, [countdownInterval]);

  return (
    <div className="App" onClick={startCountdown}>
      <h1>Hello CodeSandbox {counter}</h1>
      <h2>Start editing to see some magic happen!</h2>
    </div>
  );
}

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