useState和useEffect有什么区别?

时间:2018-11-09 02:51:19

标签: javascript reactjs react-native react-hooks

我已经看到React v16中引入了这两个新概念。

据我了解:

  

useState与带有钩子的setState类似,而useEffect的工作原理类似于生命周期方法。

我的理解正确吗?如果没有,useStateuseEffect之间的确切区别是什么?

3 个答案:

答案 0 :(得分:5)

对于useState()

首先,我们有不支持的功能组件 state,换句话说,功能组件无状态组件

现在,有了Hooks,我们有了功能组件,但是有状态。这可以通过使用useState来实现。


对于useEffect()

首先,对于无状态功能组件,我们没有组件生命周期挂钩。换句话说,每当要使用组件生命周期挂钩时,都应考虑使用类组件

现在,我们可以使用组件生命周期挂钩,而无需使用类组件。通过使用useEffect来实现。换句话说,现在无论何时我们想使用组件生命周期钩子,我们都已经有了使用 class component 或将钩子与useEffect一起使用的两个选择。


更新

  

useStateuseEffect之间的确切区别是什么?

简而言之,useState使我们以前无状态功能组件变为有状态。并且useEffect允许我们的功能组件利用组件生命周期挂钩,而这些挂钩过去仅受 class组件支持。 / p>

答案 1 :(得分:4)

简而言之,useStateuseEffect都增强了功能组件,以使它们执行类可以但功能组件(不带钩子)不能执行的操作:

  • useState允许功能组件具有状态,例如类组件中的this.state
  • useEffect允许功能组件在单个API中具有生命周期方法(例如componentDidMountcomponentDidUpdatecomponentWillUnmount)。
  • li>

请参考以下示例以进一步说明:

useState

class CounterClass extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 1 };
  }
  
  render() {
    return <div>
      <p>Count: {this.state.count}</p>
      <button onClick={() => this.setState({ 
        count: this.state.count + 1
      })}>Increase</button>
    </div>;
  }
}

function CounterFunction() {
  const [count, setCount] = React.useState(1);
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => 
        setCount(count + 1)}
      >Increase</button>
    </div>
  );
}

ReactDOM.render(
  <div>
    <CounterClass />
    <CounterFunction />
  </div>
, document.querySelector('#app'));
<script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script>

<div id="app"></div>

useEffect

class LifecycleClass extends React.Component {
  componentDidMount() {
    console.log('Mounted');
  }
  
  componentWillUnmount() {
    console.log('Will unmount');
  }
  
  render() {
    return <div>Lifecycle Class</div>;
  }
}

function LifecycleFunction() {
  React.useEffect(() => {
    console.log('Mounted');
    return () => {
      console.log('Will unmount');
    };
  }, []); // Empty array means to only run once on mount.
  return (
    <div>Lifecycle Function</div>
  );
}

ReactDOM.render(
  <div>
    <LifecycleClass />
    <LifecycleFunction />
  </div>
, document.querySelector('#app'));
<script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script>

<div id="app"></div>

在官方的React文档中了解有关useStateuseEffect的更多信息。

答案 2 :(得分:3)

useStateuseEffect是React 16.8+钩子生态系统的一部分,该生态系统旨在为功能组件提供以前仅基于类的组件(state / {{1 }}和组件生命周期方法(例如setStatecomponentDidMountcomponentDidUpdate

componentWillUnmount很简单,它使您可以在功能组件中使用状态访问器。

useState()可以组合useEffect()componentDidMountcomponentDidUpdate,但是很棘手。

您可以从hooks的官方文档中了解我在这里要讨论的大部分内容。看到钩子在起作用,比从文本中推论要容易得多。

预渲染生命周期

等同于componentWillUnmountcomponentWillReceivePropsgetDerivedStateFromProps

预渲染生命周期事件可能只是我们在返回JSX之前在功能组件中首先要做的事情(该函数本身相当于基于类的组件的componentWillMount方法。

我们不需要处理预渲染生命周期事件的钩子。

渲染后生命周期

渲染后生命周期事件,它们等同于基于类的组件中的render(…)componentDidMountcomponentDidUpdate

我们需要 ** componentDidUnmount 以处理这些渲染后生命周期事件 **,因为我们无法编写与主要组件功能内的这些生命周期事件相关联的逻辑,因为这些事件应该在组件功能将JSX(反应节点)返回到**_useEffect(…)_**渲染器之后运行。

这意味着,钩子可以做很多事情。怎么样?

我们知道react-dom,有两个参数。

  1. useEffect(fn, […watchStates]) :(必需)fn调用此函数以在每个渲染周期之后根据(2)参数给出的更改跟踪值来作为副作用运行。函数useEffect可能会返回另一个函数,该函数应在效果函数再次运行或卸载组件之前作为清理运行。
  2. fn :(可选)[…watchValues ]跟踪此数组中的值自上一个渲染周期以来已更改,然后仅调用效果useEffect。如果未指定此参数,则效果将在每个渲染周期运行。

如果我们未完全传递(2)参数,则fn中的效果逻辑将在每个渲染周期后调用。

如果我们传递带有值的(2)数组,则组件需要监视更改,并在更改时调用fn,这很容易说明。

最棘手的部分是使用空数组fn作为(2)参数,我们可以限制[]中的副作用逻辑仅在安装阶段执行,因为没有更改效果钩子将在后续渲染周期后再次触发fn的情况下进行监视。

fn

该代码段简单易懂。您可以在CodePen上试用。

要注意的一件事是,如果要在效果内部进行状态更改,请确保从监视数组中排除内部正在更改的状态。

例如,在第二个效果(用于计算鼠标移动的效果)中,我们仅通过将import React, { useState, useEffect } from "react"; export default props => { console.log("componentWillMount"); console.log("componentWillReceiveProps", props); const [x, setX] = useState(0); const [y, setY] = useState(0); const [moveCount, setMoveCount] = useState(0); const [cross, setCross] = useState(0); const mouseMoveHandler = event => { setX(event.clientX); setY(event.clientY); }; useEffect(() => { console.log("componentDidMount"); document.addEventListener("mousemove", mouseMoveHandler); return () => { console.log("componentDidUnmount"); document.removeEventListener("mousemove", mouseMoveHandler); }; }, []); // empty-array means don't watch for any updates useEffect( () => { // if (componentDidUpdate & (x or y changed)) setMoveCount(moveCount + 1); }, [x, y] ); useEffect(() => { // if componentDidUpdate if (x === y) { setCross(x); } }); return ( <div> <p style={{ color: props.color }}> Your mouse is at {x}, {y} position. </p> <p>Your mouse has moved {moveCount} times</p> <p> X and Y positions were last equal at {cross}, {cross} </p> </div> ); }; 作为第二个参数传递来触发x和y的更新,因为

  1. 从逻辑上来说,观察x和y的变化以记录鼠标的移动是正确的
  2. 如果我们不排除对moveCount的监视,则此useEffect将进入无限循环,因为我们将更新与监视变化相同的值

在我的Medium出版物中也可以找到该文章。如果您喜欢炮兵,或者有任何意见和建议,请 拍手 ,或在{{3 }}。