繁体   English   中英

react函数组件中如何实现全局变量

[英]How to implement global variable in react functional components

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

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

Class 组件


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;

问题

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

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

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

Functional components are executed from top to bottom whenever state changes, so the whole function is re-executed and that's how it returns the new JSX, compare this to class components where only render() function is executed on render, that's how functional components work .

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

解决这个问题的两种方法

将变量移动到 state

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);
    }
   .......
  };
    
    

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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