【问题标题】:How to implement global variable in react functional componentsreact函数组件中如何实现全局变量
【发布时间】: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 并将它们的返回值存储在各自的全局变量中。但是当点击“暂停”按钮时,所有全局变量的值都是“未定义”。

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

【问题讨论】:

    标签: javascript reactjs react-component react-functional-component react-class-based-component


    【解决方案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);
        }
       .......
      };
        
        
    

    【讨论】:

    • 这是有道理的。我将调试器添加到功能组件中的战略点,包括功能组件的顶部。只需单击一个按钮,就可以多次调用最顶层的调试器。这意味着函数组件被重新渲染了几次,但在每次重新渲染时,函数内的所有全局变量都被重新初始化,因此所有旧值都丢失了。感谢一百万阿舒
    猜你喜欢
    • 1970-01-01
    • 2023-04-07
    • 2021-11-13
    • 2023-01-11
    • 2019-07-18
    • 1970-01-01
    • 1970-01-01
    • 2023-01-29
    • 1970-01-01
    相关资源
    最近更新 更多