1

我是新来的反应,并决定通过使用类和功能组件实现一个简单的秒表来练习。

我使用类组件成功实现了秒表。下面是代码:

类组件


class Stopwatch extends Component {
  state = {
    status: false,
    ms: 0,
    seconds: 0,
    minutes: 0,
  };

  stopms;
  stopSeconds;
  stopMinutes;

  handleClick = () => {
    this.changeStatus();
    if (this.state.status) {
      clearInterval(this.stopms);
      clearInterval(this.stopSeconds);
      clearInterval(this.stopMinutes);
    } else {
      this.stopms = setInterval(this.changeMs, 1);
      this.stopSeconds = setInterval(this.changeSeconds, 1000);
      this.stopMinutes = setInterval(this.changeMinutes, 60000);
    }
  };

  changeStatus = () => {
    return this.setState((state) => {
      return { status: !state.status };
    });
  };

  changeMs = () => {
    return this.setState((state) => {
      if (state.ms === 99) {
        return { ms: 0 };
      } else {
        return { ms: state.ms + 1 };
      }
    });
  };

  changeSeconds = () => {
    return this.setState((state) => {
      if (state.seconds === 59) {
        return { seconds: 0 };
      } else {
        return { seconds: state.seconds + 1 };
      }
    });
  };

  changeMinutes = () => {
    return this.setState((state) => {
      if (state.seconds === 59) {
        return { minutes: 0 };
      } else {
        return { minutes: state.minutes + 1 };
      }
    });
  };

  handleReset = () => {
    clearInterval(this.stopms);
    clearInterval(this.stopSeconds);
    clearInterval(this.stopMinutes);
    this.setState({ seconds: 0, status: false, minutes: 0, ms: 0 });
  };

  componentWillUnmount() {
    clearInterval(this.stopms);
    clearInterval(this.stopSeconds);
    clearInterval(this.stopMinutes);
  }

  render() {
    return (
      <div>
        <h1>
          {this.state.minutes} : {this.state.seconds} .{" "}
          <span>{this.state.ms}</span>
        </h1>
        <button className="btn btn-lg btn-dark" onClick={this.handleClick}>
          {this.state.status === false ? "Start" : "Pause"}
        </button>
        <button className="btn btn-lg btn-dark" onClick={this.handleReset}>
          Reset
        </button>
      </div>
    );
  }
}

export default Stopwatch;

现在我正在尝试实现上面相同的代码,但使用如下所示的功能组件:

功能组件


function Stopwatch() {
  const [timeState, setTimeState] = useState({
    status: false,
    ms: 0,
    seconds: 0,
    minutes: 0,
  });

  let stopms;
  let stopSeconds;
  let stopMinutes;

  const handleClick = () => {
    changeStatus();
    if (timeState.status) {
      clearInterval(stopms);
      clearInterval(stopSeconds);
      clearInterval(stopMinutes);
    } else {
      stopms = setInterval(changeMs, 1);
      stopSeconds = setInterval(changeSeconds, 1000);
      stopMinutes = setInterval(changeMinutes, 60000);
    }
  };

  const changeStatus = () => {
    return setTimeState((prevState) => {
      return { ...prevState, status: !prevState.status };
    });
  };

  const changeMs = () => {
    return setTimeState((prevState) => {
      if (prevState.ms === 99) {
        return { ...prevState, ms: 0 };
      } else {
        return { ...prevState, ms: prevState.ms + 1 };
      }
    });
  };

  const changeSeconds = () => {
    return setTimeState((prevState) => {
      if (prevState.seconds === 59) {
        return { ...prevState, seconds: 0 };
      } else {
        return { ...prevState, seconds: prevState.seconds + 1 };
      }
    });
  };

  const changeMinutes = () => {
    return setTimeState((prevState) => {
      if (prevState.seconds === 59) {
        return { ...prevState, minutes: 0 };
      } else {
        return { ...prevState, minutes: prevState.minutes + 1 };
      }
    });
  };

  const handleReset = () => {
    clearInterval(stopms);
    clearInterval(stopSeconds);
    clearInterval(stopMinutes);
    setTimeState({ seconds: 0, status: false, minutes: 0, ms: 0 });
  };

  return (
    <div>
      <h1>
        {timeState.minutes} : {timeState.seconds} . <span>{timeState.ms}</span>
      </h1>
      <button className="btn btn-lg btn-dark" onClick={handleClick}>
        {timeState.status === false ? "Start" : "Stop"}
      </button>
      <button className="btn btn-lg btn-dark" onClick={handleReset}>
        Reset
      </button>
    </div>
  );
}

export default Stopwatch;

问题

在类组件中,我使用 handleClick 函数实现了“暂停”功能,该函数调用 clearInterval 并将其参数作为我最初声明的全局变量 stopms、stopSeconds、stopMinutes。这工作得很好,因为当秒表开始计数时,这些全局变量保存着从各自的 setInterval 返回的值。

现在在功能组件中,我通过使用“let”关键字声明相同的全局变量来复制相同的逻辑。但是“暂停”功能不起作用。当点击“开始”按钮并调用 handleClick 函数时,将调用 setIntervals 并将它们的返回值存储在各自的全局变量中。但是当点击“暂停”按钮时,所有全局变量的值都是“未定义”。

请我想知道是否有任何其他方式可以声明全局变量并使用它们在组件的整个生命周期中使用状态来保存值。

4

1 回答 1

1

每当状态发生变化时,函数式组件都会从上到下执行,因此整个函数就是这样re-executed,这就是它返回新 JSX 的方式,将其与仅render()在渲染时执行函数的类组件进行比较,这就是函数式组件的工作方式。

问题是您的全局变量实际上不是全局变量并且是函数的一部分,因此每次渲染发生时它们都会重新初始化。

解决这个问题的两种方法

将变量移动到状态

function Stopwatch() {
  const [timeState, setTimeState] = useState({
    status: false,
    ms: 0,
    seconds: 0,
    minutes: 0,
    stopms : null,
    stopSeconds : null,
    stopMinutes: null,
  });

  const handleClick = () => {
    changeStatus();
    if (timeState.status) {
      clearInterval(timeState.stopms);
      clearInterval(timeState.stopSeconds);
      clearInterval(timeState.stopMinutes);
    } else {
      let stopms = setInterval(changeMs, 1);
      let stopSeconds = setInterval(changeSeconds, 1000);
      let stopMinutes = setInterval(changeMinutes, 60000);

      setTimeState(prev => ({..prev, stopms, stopSeconds, stopMinutes})); // update the values in state
    }
  };

   ...... 

   const handleReset = () => {
    clearInterval(timeState.stopms); // use the same values to clear them
    clearInterval(timeState.stopSeconds);
    clearInterval(timeState.stopMinutes);
    .....
  };

 ..... 

 } 

或者通过将它们放在您的组件之外使它们成为全局,将工作但不推荐。

在您的组件文件中。

 // declare them just above your function
 let stopms;
 let stopSeconds;
 let stopMinutes;
  
 function Stopwatch() {
  const [timeState, setTimeState] = useState({
    status: false,
    ms: 0,
    seconds: 0,
    minutes: 0,
  });
  .....

  const handleClick = () => {
    changeStatus();
    if (timeState.status) {
      clearInterval(stopms);
      clearInterval(stopSeconds);
      clearInterval(stopMinutes);
    } else {
      stopms = setInterval(changeMs, 1);
      stopSeconds = setInterval(changeSeconds, 1000);
      stopMinutes = setInterval(changeMinutes, 60000);
    }
   .......
  };
    
    
于 2021-01-29T12:07:23.547 回答