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

Posted

技术标签:

【中文标题】react函数组件中如何实现全局变量【英文标题】:How to implement global variable in react functional components 【发布时间】:2021-05-03 08:17:34 【问题描述】:

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

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

类组件


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 并将它们的返回值存储在各自的全局变量中。但是当点击“暂停”按钮时,所有全局变量的值都是“未定义”。

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

【问题讨论】:

【参考方案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);
    
   .......
  ;
    
    

【讨论】:

这是有道理的。我将调试器添加到功能组件中的战略点,包括功能组件的顶部。只需单击一个按钮,就可以多次调用最顶层的调试器。这意味着函数组件被重新渲染了几次,但在每次重新渲染时,函数内的所有全局变量都被重新初始化,因此所有旧值都丢失了。感谢一百万阿舒

以上是关于react函数组件中如何实现全局变量的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Apollo 客户端的初始化中使用来自 React 的全局数据?

如何在微信小程序定义全局变量全局函数如何实现 函数复用 模块化开发等问题详解

如何在 React 中调用全局函数?

一个函数如何实现return好几个返回值

如何将material-ui Snackbar实现为全局函数?

如何使用 React Hooks 实现复杂组件的状态管理