精华内容
下载资源
问答
  • react useEffect

    2020-11-03 22:11:49
    函数组件中没有生命周期,那么可以使用 useEffect 来替代。如果你熟悉 React class 的生命周期函数,你可以把 useEffect Hook 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的...

    useEffect

    函数组件中没有生命周期,那么可以使用 useEffect 来替代。如果你熟悉 React class 的生命周期函数,你可以把
    useEffect Hook 看做 componentDidMount,componentDidUpdate 和
    componentWillUnmount
    这三个函数的组合。

    react 示例

    class Example extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          count: 0
        };
      }
      componentDidMount() {
        document.title = `You clicked ${this.state.count} times`;
      }
      componentDidUpdate() {
        document.title = `You clicked ${this.state.count} times`;
      }
      render() {
        return (
          <div>
            <p>You clicked {this.state.count} times</p>
            <button onClick={() => this.setState({ count: this.state.count + 1 })}>
              Click me
            </button>
          </div>
        );
      }
    }
    

    Hook 示例

    import React, { useState, useEffect } from 'react';
    
    function Example() {
      const [count, setCount] = useState(0);
    
      useEffect(() => {
        document.title = `You clicked ${count} times`;
      });
    
      return (
        <div>
          <p>You clicked {count} times</p>
          <button onClick={() => setCount(count + 1)}>
            Click me
          </button>
        </div>
      );
    }
    

    你可以通知 React 跳过对 effect 的调用,只要传递数组作为 useEffect 的第二个可选参数即可,如果想执行只运行一次的
    effect(仅在组件挂载和卸载时执行),可以传递一个空数组([])作为第二个参数。这就告诉 React 你的 effect 不依赖于
    props 或 state 中的任何值,所以它永远都不需要重复执行

    useEffect(() => {
      document.title = `You clicked ${count} times`;
    }, [count]); // 仅在 count 更改时更新
    
    展开全文
  • 引言 Hooks 是 React 16.8 的新增特性,至今经历两年的时间,它可以让你在不编写 class 组件的情况下使用 state 以及其他 React 特性。useEffect 是...

    引言

    Hooks 是 React 16.8 的新增特性,至今经历两年的时间,它可以让你在不编写 class 组件的情况下使用 state 以及其他 React 特性。useEffect 是基础 Hooks 之一,我在项目中使用较为频繁,但总有些疑惑 ,比如:

    • 如何正确使用 useEffect

    • useEffect 的执行时机 ?

    • useEffect 和生命周期的区别 ?

    本文主要从以上几个方面分析 useEffect ,以及与另外一个看起来和 useEffect 很像的 Hook useLayoutEffect 的使用和它们之间的区别。

    useEffect 简介

    首先介绍两个概念,纯函数和副作用函数。纯函数( Pure Function ):对于相同的输入,永远会得到相同的输出,而且没有任何可观察的副作用,这样的函数被称为纯函数。副作用函数( Side effect Function ):如果一个函数在运行的过程中,除了返回函数值,还对主调用函数产生附加的影响,这样的函数被称为副作用函数。useEffect 就是在 React 更新 DOM 之后运行一些额外的代码,也就是执行副作用操作,比如请求数据,设置订阅以及手动更改 React 组件中的 DOM 等。

    正确使用 useEffect

    基本使用方法:useEffect(effect)根据传参个数和传参类型,useEffect(effect) 的执行次数和执行结果是不同的,下面一一介绍。

    • 默认情况下,effect 会在每次渲染之后执行。示例如下:

    useEffect(() => {
      const subscription = props.source.subscribe();
      return () => {
        // 清除订阅
        subscription.unsubscribe();
      };
    });
    
    • 也可以通过设置第二个参数,依赖项组成的数组  useEffect(effect,[]) ,让它在数组中的值发生变化的时候执行,数组中可以设置多个依赖项,其中的任意一项发生变化,effect 都会重新执行。示例如下:

    useEffect(
      () => {
        const subscription = props.source.subscribe();
        return () => {
          subscription.unsubscribe();
        };
      },
      [props.source],
    );
    

    需要注意的是:当依赖项是引用类型时,React 会对比当前渲染下的依赖项和上次渲染下的依赖项的内存地址是否一致,如果一致,effect 不会执行,只有当对比结果不一致时,effect 才会执行。示例如下:

    function Child(props) {
      
      useEffect(() => {
        console.log("useEffect");
      }, [props.data]);
      
      return <div>{props.data.x}</div>;
    }
    
    let b = { x: 1 };
    
    function Parent() {
      const [count, setCount] = useState(0);
      console.log("render");
      return (
        <div>
          <button
            onClick={() => {
              b.x = b.x + 1;
              setCount(count + 1);
            }}
          >
            Click me
          </button>
          <Child data={b} />
        </div>
      );
    }
    

    结果如下:


    上面实例中,组件 <Child/> 中的 useEffect 函数中的依赖项是一个对象,当点击按钮对象中的值发生变化,但是传入 <Child/>  组件的内存地址没有变化,所以 console.log("useEffect") 不会执行,useEffect 不会被打印。为了解决这个问题,我们可以使用对象中的属性作为依赖,而不是整个对象。把上面示例中组件 <Child/> 修改如下:

    function Child(props) {
      
      useEffect(() => {
        console.log("useEffect");
      }, [props.data.x]);
      
      return <div>{props.data.x}</div>;
    }
    

    修改后结果如下:


    可见 useEffect 函数中的 console.log("useEffect") 被执行,打印出 useEffect。

    • 当依赖项是一个空数组 [] 时 , effect 只在第一次渲染的时候执行。

    useEffect 的执行时机

    默认情况下,effect 在第一次渲染之后和每次更新之后都会执行,也可以是只有某些值发生变化之后执行,重点在于是每轮渲染结束后延迟调用( 异步执行 ),这是 useEffect 的好处,保证执行 effect 的时候,DOM 都已经更新完毕,不会阻碍 DOM 渲染,造成视觉阻塞。

    useEffect 和 useLayoutEffect 的区别

    useLayoutEffect 的使用方法和 useEffect 相同,区别是他们的执行时机。

    如上面所说,effect 的内容是会在渲染 DOM 之后执行,然而并非所有的操作都能被放在 effect 都延迟执行的,例如,在浏览器执行下一次绘制前,需要操作 DOM 改变页面样式,如果放在 useEffect 中执行,会出现闪屏问题。而 useLayoutEffect 是在浏览器执行绘制之前被同步执行,放在 useLayoutEffect 中就会避免这个问题。

    这篇文章中可以清楚的看到上述例子的具体实现:useEffect 和 useLayoutEffect 的区别

    对比 useEffect 和生命周期

    如果你熟悉生命周期函数,你可能会用生命周期的思路去类比思考 useEffect 的执行过程,但其实并不建议这么做,因为 useEffect 的心智模型和 componentDidMount 等其他生命周期是不同的。

    Function 组件中不存在生命周期,React 会根据我们当前的 props 和 state 同步 DOM ,每次渲染都会被固化,包括 state、props、side effects 以及写在 Function 组件中的所有函数。

    另外,大多数 useEffect 函数不需要同步执行,不会像 componentDidMountcomponentDidUpdate 那样阻塞浏览器更新屏幕。

    所以 useEffect 可以被看作是每一次渲染之后的一个独立的函数 ,可以接收 props 和 state ,并且接收的 props 和 state 是当次 render 的数据,是独立的 。相对于生命周期 componentDidMount 中的 this.state 始终指向最新数据, useEffect 中不一定是最新的数据,更像是渲染结果的一部分 —— 每个 useEffect 属于一次特定的渲染。对比示例如下:

    • 在 Function 组件中使用  useEffect  代码示例 (点击在线测试):

    function Counter() {
      const [count, setCount] = useState(0);
    
      useEffect(() => {
        setTimeout(() => {
          console.log(`You clicked ${count} times`);
        }, 3000);
      });
    
      return (
        <div>
          <p>You clicked {count} times</p>
          <button onClick={() => setCount(count + 1)}>
            Click me
          </button>
        </div>
      );
    }
    

    结果如下:


    • 在 Class 组件中的使用生命周期,代码示例:

      componentDidUpdate() {
        setTimeout(() => {
          console.log(`You clicked ${this.state.count} times`);
        }, 3000);
      }
    

    结果如下:


    但是每次渲染之后都去执行 effect 并不高效。所以怎么解决呢 ?这就需要我们告诉 React 对比依赖来决定是否执行 effect

    如何准确绑定依赖

    effect 中用到了哪些外部变量,都需要如实告诉 React ,那如果没有正确设置依赖项会怎么样呢 ?示例如下 :


    上面例子中, useEffect 中用到的依赖项 count,却没有声明在卸载依赖项数组中,useEffect 不会再重新运行(只打印了一次 useEffect ), effectsetInterVal 拿的 count 始终是初始化的 0 ,它后面每一秒都会调用 setCount(0 + 1) ,得到的结果始终是 1 。下面有两种可以正确解决依赖的方法:

    1.在依赖项数组中包含所有在 effect 中用到的值

    effect 中用到的外部变量 count 如实添加到依赖项数组中,结果如下:


    可以看到依赖项数组是正确的,并且解决了上面的问题,但是也可以发现,随之带来的问题是:定时器会在每一次 count 改变后清除和重新设定,重复创建/销毁,这不是我们想要的结果。

    2.第二种方法是修改 effect 中的代码来减少依赖项

    即修改 effect 内部的代码让 useEffect 使得依赖更少,需要一些移除依赖常用的技巧,如:setCount 还有一种函数回调模式,你不需要关心当前值是什么,只要对 “旧的值” 进行修改即可,这样就不需要通过把 count 写到依赖项数组这种方式来告诉 React 了,因为 React 已经知道了。


    是否需要清除副作用

    若只是在 React 更新 DOM 之后运行一些额外的代码,比如发送网络请求,手动变更 DOM,记录日志,无需清除操作,因为执行之后就可以被忽略。

    需要清除的是指那些执行之后还有后续的操作,比如说监听鼠标的点击事件,为防止内存泄漏清除函数将在组件卸载之前调用,可以通过 useEffect 的返回值销毁通过 useEffect 注册的监听。

    清除函数执行时机是在新的渲染之后进行的,示例如下(点击在线测试):

    const Example = () => {
      const [count, setCount] = useState(0);
    
      useEffect(() => {
        console.log("useEffect");
        return () => {
          console.log("return");
        };
      }, [count]);
    
      return (
        <div>
          <p>You Click {count} times </p>
          {console.log("dom")}
          <button
            onClick={() => {
              setCount(count + 1);
            }}
          >
            Click me
          </button>
        </div>
      );
    };
    
    

    结果如下:


    需要注意的是useEffect 的清除函数在每次重新渲染时都会执行,而不是只在卸载组件的时候执行 。

    参考文档

    React Core Team 成员、Readux 作者 Dan 对 useEffect 的完全解读  ---  A Complete Guide to useEffect

    1. JavaScript 重温系列(22篇全)

    2. ECMAScript 重温系列(10篇全)

    3. JavaScript设计模式 重温系列(9篇全)

    4. 正则 / 框架 / 算法等 重温系列(16篇全)

    5. Webpack4 入门(上)|| Webpack4 入门(下)

    6. MobX 入门(上) ||  MobX 入门(下)

    7. 120+篇原创系列汇总

    回复“加群”与大佬们一起交流学习~

    点击“阅读原文”查看 120+ 篇原创文章

    展开全文
  • class Example extends React.Component { componentDidMount() { console.log('Did mount!'); } render() { return null; } } 在函数组件中,我们可以使用useEffect这样编写 function Example() { // 注意...
    componentDidMount vs useEffect

    类组件中,我们这样编写componentDidMount:

    class Example extends React.Component {
      componentDidMount() {
        console.log('Did mount!');
      }
      render() {
        return null;
      }
    }
    

    在函数组件中,我们可以使用useEffect这样编写

    function Example() {
      // 注意不要省略第二个参数 [],这个参数保证函数只在挂载的时候进行,而不会在更新的时候执行。
      useEffect(() => console.log('mounted'), []);  
      return null;
    }
    
    componentDidUpdate vs useEffect

    类组件中,我们这样编写componentDidUpdate:

    componentDidMount() {
      console.log('mounted or updated');
    }
    
    componentDidUpdate() {
      console.log('mounted or updated');
    }
    

    而在函数组件中,我们使用useEffect起到同样的效果:

    useEffect(() => console.log('mounted or updated'));  // 不需要指定第二个参数
    

    值得一提的是,现在官方推荐的编程规范就是不区分 update 阶段和 mount 阶段,两个阶段视为一致。

    componentWillUnmount vs useEffect

    类组件中,我们这样编写componentWillUnmount:

    componentWillUnmount() {
      console.log('will unmount');  
    }
    

    而在函数组件中,我们使用useEffect起到同样的效果:

    useEffect(() => {
      return () => {
        console.log('will unmount');  // 直接使用return返回一个函数,这个函数在unmount时执行。
      }
    }, []);
    

    你也可以使用useEffect 组合componentDidMount 和 componentDidUnmount。

    useEffect(()=>{
    	console.log("mounted")return () => {
    		console.log("unmounted");
    	}
    }, [Started])  // 前后两次执行的Started相等时,useEffect代码生效,否则跳过。
    
    展开全文
  • useEffect的使用 componentDidMount()组件第一次渲染完成,此时dom节点已经生成,可以在这里调用ajax请求,返回数据setState后组件会重新渲染 componentWillUnmount ()在此处完成组件的卸载和数据的销毁。 clear你...

    useEffect的使用

    componentDidMount()组件第一次渲染完成,此时dom节点已经生成,可以在这里调用ajax请求,返回数据setState后组件会重新渲染

    componentWillUnmount ()在此处完成组件的卸载和数据的销毁。

    • clear你在组建中所有的setTimeout,setInterval
    • 移除所有组建中的监听 removeEventListener

    componentDidUpdate组件更新完毕后,react只会在第一次初始化成功会进入componentDidmount,之后每次重新渲染后都会进入这个生命周期,这里可以拿到prevProps和prevState,即更新前的props和state。

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
      <title id="title">useEffect的使用</title>
      <script src="https://cdn.bootcdn.net/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
      <script src="https://cdn.bootcdn.net/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
      <script src="https://cdn.bootcdn.net/ajax/libs/babel-standalone/7.0.0-beta.3/babel.min.js"></script>
      <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    </head>
    <body>
      <div id="example"></div>
      <script type="text/babel">
        const researchToSelect = React.createElement(() => {
          const { useEffect, useRef, useState } = window.React;
         const [count,setCount] = useState(0)
    
            useEffect(()=>{
                // 相当于 componentDidMount
                console.log(document.getElementById('btn'),1)
    
                return ()=>{
                	// 相当于 componentWillUnmount,在这个阶段,该dom节点将销毁,				下面的不会打印
                    console.log(document.getElementById('btn'),2)
                }
    			//不打印
                console.log(document.getElementById('btn'),3)
            },[])
    
            const btnClick = ()=>{
                console.log(count)
            }
    
            useEffect(()=>{
             	// 相当于 componentDidUpdate
                document.getElementById('num').innerHTML = count
            })
          
    
          return  <div className="content">
                <button id="btn" onClick={btnClick}>
                	点击
                	<span id="num">{count}</span>
                </button>
          </div>
        })
    
      ReactDOM.render(researchToSelect,document.getElementById("example"));
      </script>
    </body>
    </html>
    
    展开全文
  • 使用 useEffect 方法替代生命周期API 4. 推荐阅读 如果你想要: 完整了解整个组件生命周期和对应函数API,那么请从头开始; 快速理解传统生命周期函数API的含义和使用,那么点击这里; 快速了解如何在函数组件中...
  • React useEffect+useState ?

    2021-02-19 11:58:10
    useState 01:useState返回一个数组[值,函数] 02:首次渲染的时候被创建(重复渲染不会再次创建),React会在重复渲染时记住它当前的值,并且再次渲染时,提供最新的值给我们的函数 ...useEffect info1:不...
  • react组件生命周期 更新生命周期 (Update lifecycle) The previous article was about the lifecycle hooks of a class-based component when it is being created. In this article, we will see the hooks that ...
  • react useEffect的父子组件执行顺序

    千次阅读 2020-08-14 14:15:23
    Component写法下父子组件的生命周期执行顺序 初次挂载 父constructor 父componentWillMount 父render 子constructor 子componentWillMount 子render 子componentDidMount 父componentDidMount useEffect 可以...
  • react组件生命周期 生命周期挂钩 (Lifecycle hooks) After React Hooks were introduced in React 16.8, lifecycle hooks — which were only available in class-based components, are now also available in ...
  • 简单介绍一下hook中useEffect在函数组件使用可以代替类组件中的哪些生命周期和写法 1、useEffect代替componentDidMount的写法 类组件的写法 class Example extends React.Component { componentDidMount() { ...
  • 主线什么是 Effect与生命周期的关系运行时机类实际工作的例子 - 抓取数据第二个参数添加其他属性划重点 useEffectReact Hooks 的核心,要保证理解它的运行机制和正确的使用方法才能避免这样那样的坑。在以前的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,569
精华内容 1,027
热门标签
关键字:

reactuseeffect生命周期