useState 和 useEffect 有什么区别?

What’s the difference between useState and useEffect?

我在react v16中看到了这两个新概念

据我了解:

useState is similar like setState with hooks and useEffect works similarly like life cycle methods.

我的理解对吗?如果不是,useStateuseEffect 之间的确切区别是什么?

对于useState()

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

现在,有了 Hooks,我们有了 功能组件 有状态 。它是通过使用 useState.

来实现的

对于useEffect()

首先,对于无状态功能组件,我们没有组件生命周期挂钩。换句话说,每当你想使用组件生命周期钩子时,你应该考虑使用class组件.

现在,我们可以在不使用class组件的情况下使用组件生命周期钩子。它是通过使用 useEffect 来实现的。换句话说,现在每当我们想使用 component lifecycle hooks 时,我们已经有两个选择,要么使用 class component 要么使用 Hooks useEffect.


更新

what’s the exact difference between useState and useEffect?

简单来说,useState 允许我们的 功能组件 曾经是 无状态的 变成 有状态的useEffect 允许我们的 功能组件 利用 组件生命周期挂钩 ,在过去,只有 支持class 个组件.

简单来说,useStateuseEffect 都增强了功能组件,使它们可以做 classes 可以但功能组件(没有钩子)不能的事情:

  • useState 允许功能组件具有 状态 ,就像 class 组件中的 this.state
  • useEffect 允许功能组件将 生命周期方法 (例如 componentDidMountcomponentDidUpdatecomponentWillUnmount)合二为一单身 API.

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

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 文档中阅读有关 useState and useEffect 的更多信息。

useStateuseEffect 是 React 16.8+ hooks 生态系统的一部分,旨在为功能组件提供相同的功能,而这些功能以前只有基于 class 的组件才可用(state / setState 和组件生命周期方法(例如 componentDidMountcomponentDidUpdatecomponentWillUnmount

useState() 是直截了当的,它让你在功能组件中拥有状态访问器。

useEffect() 可以组合 componentDidMountcomponentDidUpdatecomponentWillUnmount,但很棘手。

你可以从 hooks 的官方文档中解读我在这里要讨论的大部分内容。在工作中看到钩子比从文本推理更容易。

预渲染生命周期

预渲染生命周期 事件相当于 componentWillReceivePropsgetDerivedStateFromPropscomponentWillMount 可以只是我们在功能中首先做的事情returning JSX(react-node)之前的组件作为函数本身等同于 render(…) 基于 class 的组件的方法。

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

Post-渲染生命周期

Post-渲染生命周期事件,相当于[=117=中的componentDidMountcomponentDidUpdatecomponentDidUnmount ] 基于组件。

我们需要 ****_useEffect(…)_** 来处理这些Post-render生命周期事件** 因为我们不能在主组件函数中编写与这些生命周期事件相关的逻辑,因为这些应该在组件函数returns JSX(反应节点)之后运行 ) 到 react-dom 渲染器。

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

我们知道 useEffect(fn, […watchStates]),接受 2 个参数。

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

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

如果我们传递 (2) 带有值的数组,组件需要观察变化,并在变化时调用 fn,非常不言自明。

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

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

代码片段简单明了。您可以在 CodePen.

上试用

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

例如,在第二个效果(计算鼠标移动的效果)中,我们只在 x 和 y 更新时触发它,通过将 [x , y] 作为第二个参数传递,因为

  1. 观察 x 和 y 的变化以记录鼠标移动在逻辑上是正确的
  2. 如果我们不排除 moveCount 被监视,这个 useEffect 将进入无限循环,因为我们将更新同样的值,我们也在监视变化

这篇文章也可以在我的 Medium publication. If you like the artile, or have any comments and suggestions, please clap or leave comments on Medium.

上找到