• react常用hooks总结


    React Hooks常用总结

    react常用hooks

    Hooks简介

    概述

    Hooks是React16.8的新增特性。它可以让你在不编写class的情况下使用state,以及其他的React特性,用简单的话来说,React
    Hooks就是一些React提供的内置函数,这些函数可以让Function Component和Class
    Component一样能够拥有组件状态(state)以及进行副作用(side effect)。

    优势

    • a. 完全可选的,你无需重新写任何已有的代码就可以在一些组件中尝试Hooks。
    • b. 100%向后兼容,Hooks不包含任何破坏性改动。
    • c. 现在可用,b16.8版本已经发布。

    虽然现在有了React Hooks,但是React中的class是没有计划移除的。Class组件之间难以服用状态逻辑,复杂组件难以理解,使用class类组件导致学习成本变高,生命周期逻辑难以维护,同一个生命周期中包含不同逻辑,同一类逻辑分散在不同生命周期。父组件给子组件传递函数时,必须绑定this。

    react中的组件四种绑定this的方法区别:

    <button onClick={this.handleClick2}>btn1</button>
    <button onClick={this.handleClick1.bind(this)}>btn2</button>
    <button onClick={() => this.handleClick1()}>btn3</button>
    <button onClick={this.handleClick3}>btn4</button>
    
    • 1
    • 2
    • 3
    • 4
    • 第一种在构造函数中绑定this,那么每次父组件刷新的时候,如果传递给子组件其他的Props值不变,那么子组件就不会刷新。
    • 第二种是在render()函数里面绑定this,因为bind函数会返回一个新的函数,所以每次父组件刷新时,都睡重新生成一个函数,即使父组件传递给子组件其他的props值不变,子组件每次都会刷新。
    • 第三种是使用箭头函数,父组件刷新的时候,即使两个箭头函数的函数体都是一样的,都会生成一个新的箭头函数,所以子组件每次都会刷新
    • 第四种是使用类的静态属性,原理和第一种差不多,但是比第一种更简洁。

    Hooks的优势

    1. 能优化类组件的三大问题。
    2. 能在无需修改组件结构的情况下复用状态逻辑(自定义hooks) 。
    3. 能将组件中相互关联的部分拆分成更小的函数。

    副作用的关注点分离,副作用指那些没有发生在数据向视图转换过程中的逻辑,入Ajax请求,访问原生dom元素,本地持久化缓存,绑定/解绑时间,添加订阅,设置定时器,记录日志,以往这些副作用都是写在类组件生命周期函数中的,而useEffect在全部渲染完毕后才会执行,userLayoutEffect会在浏览器layout之后,painting之前执行。

    Hooks使用注意事项

    1. 只能在函数内部的最外层调用hooks,不要再循环,条件判断或者子函数中调用。
    2. 只能在React的函数组件中调用Hook,不要再其他JS函数中调用。

    Hooks功能概览

    在 React 的世界中,不同的 hooks 用处也是不同的,对 React hooks 按照功能分类,分成了 数据更新驱动,状态获取与传递,执行副作用,状态派生与保存,和工具类型, 具体功能划分和使用场景如下图:
    Hooks功能概览

    常见的Hooks

    1. userState

    useState可以让函数组件像类组件那样拥有state,函数组件通过userState可以让组件重新渲染,更新视图。

    const [xxx,setxxxx] = React.useState(initValue)
    
    • 1

    参数一、xxx,目的提供给UI,座位渲染视图的数据源。
    参数二、setxxx,改变state的函数推动函数组件渲染的渲染函数。
    参数三、initValue,两种类型,第一种情况是非函数,将作为state初始化的值,第二种情况是函数,函数返回值作为useState初始化的值。

    使用示例

    import React,{useState} from "react"
    function User(){
      const [count,setCount] = useState(5) // 默认值为0
      return (
        <div>
          count:{count}
          <br/>
          {/* 使setCount让count+1操作 */}
          <button onClick={ ()=> {setCount(count+1)} } >改变count</button>
        </div>
      );
    }
    export default User
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    userState使用注意:
    • a.在函数组件执行一次上下文中,state的值不变。
    • b.如果两次setxxx传入的值相同,那么组件就不会更新。
    • c.当触发setxxx在当前执行上下文中获取不到最新的state,只有在下一次组件render中才能获取到。

    2. useReducer

    在使用React的时候,如果遇到了状态管理,一般会用到Redux,而React本身不提供状态管理的,但是useReducer提供了状态管理。userReducer是react-hooks提供的能够在无状态组件中运行的类似redux的功能api。

    const [state,dispatch] = useReducer(reducer,initialState)
    
    • 1

    参数一、state更新之后的state值。
    参数二、派发更新的dispatchAction函数,本质上和useState的dispatchAction是一样的。
    参数三、一个函数reducer,我们可以认为他就是一个redux中的reducer,reducer的参数就是常规的reducer里面的state和action,返回改变后的state,这里需要注意的是,如果返回的state和之前的state,内存指向相同,那么组件将不会更新。

    userReducer基本使用1:

    import { useReducer } from "react";
    /* 当state需要维护多个数据且它们互相依赖时,推荐使用useReducer
    组件内部只是写dispatch({...})
    处理逻辑的在useReducer函数中。获取action传过来的值,进行逻辑操作
    */
    
    // reducer计算并返回新的state
    function reducer(state, action) {
      const { type, nextName } = action;
      switch (type) {
        case "ADD":
          return {
            ...state,
            age: state.age + 1
          };
        case "NAME":
          return {
            ...state,
            name: nextName
          };
      }
      throw Error("Unknown action: " + action.type);
    }
    
    export default function ReducerTest() {
      const [state, dispatch] = useReducer(reducer, { name: "wuyong", age: 12 });
      function handleInputChange(e) {
        dispatch({
          type: "NAME",
          nextName: e.target.value
        });
      }
      function handleAdd() {
        dispatch({
          type: "ADD"
        });
      }
      const { name, age } = state;
      return (
        <>
          <input value={name} onChange={handleInputChange} />
          <br />
          <button onClick={handleAdd}>添加1</button>
          <p>
            Hello,{name}, your age is {age}
          </p>
        </>
      );
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    效果:
    react的userReducer

    userReducer基本使用2:

    import React,{useReducer} from "react"
    const User = ()=> {
      const [num,dispatchNum] = useReducer((state,action)=>{
        const { resetNum , name } = action
        switch(name){
          case "add":
            return state+1        
          case "sub":
            return state-1        
          case "reset":
            return  resetNum
        }
        return state
      },0)
      return (
        <div>
          count:{num}
          <br/>
          <button onClick={()=>dispatchNum({ name:'add' })} >增加</button>
          <button onClick={()=>dispatchNum({ name:'sub' })} >减少</button>
          <button onClick={()=>dispatchNum({ name:'reset' ,resetNum:0 })} >重置</button>
    
        </div>
      );
    }
    export default User
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    效果:
    react的userReducer

    3. useSyncExternalStore

    useSyncExternalStore 能够让React组件在concurrent模式下安全地有效地读取外界数据源,在组件渲染过程中能够检测到变化,并且在数据源发生变化时侯,能够调度更新。当读取到外部状态发生了变化,会触发一个强制更新,来保证结果的一致性。

    useSyncExternalStore(subscribe,getSnapshot,getServerSnapshot)
    
    • 1

    参数一、subscribe为订阅数,当数据发生改变的时候,会触发subscribe,在useSyncExternalStore会通过带有记忆性的getSnapshot来判别数据是否发生变化,如果发生变化,那么会强制更新数据。
    参数二、getSnapshot可以理解成一个带有记忆功能的选折起,当store变化的时候,会通过getSnapshot生成新的状态值,这个状态值可提供给组件数据源使用,getSnapshot可以检查订阅的值是否发生改变,改变的话那么会触发更新。
    参数三、getServerSnapshot用于hydration模式下的getSnapshot。

    4. useEffect

    当组件init,dom render完成操纵dom,请求数据(如componentDidMount)等;不限制条件,组件每次更新都会触发useEffect --> componentDidUpdate与componentWillreceiveprops;

    useEffect(()=>{return destory},dep)
    
    • 1

    参数一、第一个参数callback回调,返回的desotry作为下一次callback执行之前调用,用于清楚上一次callback产生的副作用。第一个参数为处理事件,第二个参数为接受数组,为限定条件,当数组发生变化时,触发事件,为数组只在组件初始化时触发。
    参数二、作为依赖项,是一个数组,可以有多个依赖项,依赖项改变,执行上一次回调返回的destory,和执行新的effect第一个参数callback。

    对于useEffect执行,react处理逻辑是采用异步调用,面对每一个effect的callback,react会向setTimeout回调函数一样,放入任务列队,等到主线程任务完成,DOM更新,js执行完成,绘图绘制完成才执行,所以effect回调函数不会阻塞浏览器绘制视图。
    注意:useEffect⽆法直接使⽤async await

    import React, { useState, useRef } from "react"
    import { useEffect } from "react"
    /* 模拟数据交互 */
    function getUserInfo(a) {
      return new Promise((resolve) => {
        setTimeout(() => {
          resolve({
            name: a,
            age: 16
          })
        }, 500)
      })
    }
    const SwitchPage = ({ a }) => {
      const [userMessage, setUserMessage] = useState({})
      const div = useRef()
      const [number, setNumber] = useState(0)
      // 模拟事件处理函数
      const handleResize = () => { }
      // useEffect使用
      useEffect(() => {
        // 请求数据
        getUserInfo(a).then(res => {
          console.log("请求数据")
          setUserMessage(res)
        })
        // 定时器,延时器
        const timer = setInterval(() => console.log("定时器", 666), 1000)
        // 操作dom
        console.log(div.current)
        // 事件监听
        window.addEventListener('resize', handleResize)
        // 消除副作用,定时器,延时器
        return () => {
          /*
              只有当props->a和state->number改变的时候 ,useEffect副作⽤函数重新执⾏ ,
              如果此时数组为空[],证明函数只有在初始化的时候执⾏⼀次相当于componentDidMount
          */
          clearInterval(timer)
          window.removeEventListener('resize', handleResize)
        }
      }, [a, number])
      return (<div ref={div} >
        <span>{userMessage.name}</span>
        <span>{userMessage.age}</span>
        <div onClick={() => setNumber(1)} >{number}</div>
      </div>)
    }
    export default SwitchPage
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    在这里插入图片描述

    执行功能:

    1.操作DOM
    2.请求数据
    3.注册事件监听器, 事件绑定
    4.清除定时器,事件解绑

    5.useLayoutEffect

    渲染更新之前的,如果你希望界面必须在执行完一些业务代码后才进行渲染,那么可以把代码放到useLayoutEffect的effect中。首先useLayoutEffect在DOM更新之后,浏览器绘制之前,这样可以方便修改DOM,获取DOM信息,这样浏览器智慧绘制一次,如果修改DOM布局放在useEffect,那么useEffect执行是在浏览器绘制视图之后,接下来又更改DOM,这样会导致浏览器再次回流和重绘,而且由于这两次重绘,视图上可能会照成闪现突兀的效果。

    • useEffect : 组件更新挂在完成->浏览器绘制完成->执行useEffect回调。
    • useLayoutEffect : 组件更新挂载完成 -> 执⾏useLayoutEffect回调-> 浏览器dom 绘制完成;callback中代码执行会阻塞浏览器绘制。
    import React, { useState, useRef } from "react"
    import { useEffect,useLayoutEffect } from "react"
    const SwitchPage = () => {
      const [count,setCount] = useState(0)
      useEffect(()=>{
        if(count===0){
          const randomNum = 111
          setCount(111)
        }
      },[count])
      useLayoutEffect(()=>{
        if(count === 0){
          const randomNum = 111
          setCount(111)
        }
      },[count])
      return (
      <div>
        <div onClick={()=>setCount(0)}>{count}</div>
      </div>)
    }
    export default SwitchPage
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    6. useRef

    用来获取元素,缓存数据,入参可以作为初始值。

    import React, { useState, useRef } from "react"
    import { useEffect,useLayoutEffect } from "react"
    const SwitchPage = () => {
      const dom = useRef(null)
      const handerSubmit = () =>{
        console.log(dom.current)
      }
      return (
      <div>
        <div ref={dom} >表单组件</div>
        <button onClick={()=>handerSubmit()}>提交</button>
      </div>)
    }
    export default SwitchPage
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    7. useContext

    const contextValue = useContext(context)
    
    • 1

    用来获取父级组件传递过来的context值,这个当前值就是最近的父级组件Provider的value;可以使用useContext来获取父级组件传递过来的context值,这个当前值就是最近的父级组件,Provider设置的value值,useContext参数一般是由createContext方式创建的,也可以父级上下文context传递的【参数为context】。useContext可以替代context.Consumer来获取Provider中保存的value值。
    当数据进行(父子或者爷爷传递孙子时)多成绩数据传递的时候,在函数组件中我们可以运用useContext进行数据传输。

    举个简单的demo例子,子组件通过按钮修改爷爷组件中的num值:
    目录结构:
    在这里插入图片描述

    ContextDemo代码:

    import React , {createContext} from 'react'
    
    const DemoContext = createContext()
    
    export default DemoContext
    
    • 1
    • 2
    • 3
    • 4
    • 5

    GrandDad代码:

    import React,{useState} from "react"
    import Father from './Father'
    import DemoContext from "./ContextDemo"
    function GrandDad(){
      const[num,setNum] = useState(0)
      const handleAddClick=()=>{
        setNum(num=>num+1)
      }
      const handleSubClick=()=>{
        setNum(num=>num-1)
      }
      return <div style={{background:"yellow"}}>
        爷爷组件
        <DemoContext.Provider value={{num,handleAddClick,handleSubClick}}>
          {num}<br/>
          <Father></Father>
        </DemoContext.Provider>
      </div>
    }
    export default GrandDad
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    Father代码:

    import React  from "react"
    import Son from './Son'
    function Father(){
      return <div style={{background:"red"}}>
        爸爸组件<br />
      <Son></Son>
      </div>
    }
    export default Father
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    son代码:

    import React,{useContext}  from "react"
    import DemoContext from './ContextDemo'
    function Son(){
      const variable = useContext(DemoContext)
      console.log("儿子组件",variable)
      return <div style={{background:"pink"}}>
        儿子组件<br />
        <div>
          <button onClick={variable.handleSubClick}>-</button>
          {variable.num}
          <button onClick={variable.handleAddClick}>+</button>
        </div>
      </div>
    }
    export default Son
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    效果:
    在这里插入图片描述
    在这里插入图片描述

    8. useMemo

    useMemo它是用来作为一个缓存使用,只有当一个依赖项改变的时候才会发生变化,否则拿缓存的值,就不用在每次渲染的时候在做计算,这种有助于避免在每次渲染的时候进行高开销计算,uerMemo的函数在函数渲染期间执行,所以不该在此期间做的操作请去除,如果没有提供依赖项数据,每次都会重新计算值,相当于没有优化了。

    • 使用场景一、当登录之后,你的个人信息是不会变的,当你退出登录,重新输入另外一个人的账号密码之后,这个时候个人信息就会改变了,那这样我就可以把账号和密码作为两个依赖项,当他们改变的时候,就更新个人信息,否则拿缓存的值,宠儿达到优化的目的。
    • 使用场景二、有时候需要从A页面跳转到B页面,并携带一些参数,那么就会从路由下手,http://localhost:3000/home/:id/:name或者http://localhost:3000//home?id=123&name=wy,然后B页面基于这些参数做一些查询操作,那路由不变,其实这些查询出来的数据应该是不变的(当然其中包含增删改操作除外),那就可以把这些路由参数当作依赖项,只有依赖项变了,该页面的数据才会变。

    简单demo例子

    import React,{useState,useMemo} from "react"
    function Demo2(){
      const [count,setCount] = useState(10)
      const [value,setValue] = useState(10)
      let memoMessage = useMemo(()=>{
        console.log("useMemo....")
        return `memoMessage will change with ${count}`
      },[count])
      function hangleClick(name){
        if(name==="count"){
          setCount(count + 1)
          setValue(value + 1)
        }else{
          setValue(value + 1)
        }
      }
      return <div>
        <p>{memoMessage}</p>
        <h3>count:{count}</h3>
        <h3>value:{value}</h3>  
        <br/>
        <button onClick={()=>{hangleClick("count")}}>count</button>
        <button onClick={()=>{hangleClick("value")}}>value</button>
      </div>
    }
    export default Demo2
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    react hooks常用hooks
    react hooks常用hooks

    9. useCallback

    函数相等性检查,使用useCallback优化代码,是对传过来的回调函数优化,返回的是一个函数,useMemo可以返回任何值,函数,对象等都可以,简单的来说就是返回一个函数,只有在依赖项发生变化的时候才会更新返回一个函数。

    React.useCallback(function,array)
    
    • 1

    使用场景:当父组件传入子组件函数时,由于React.memo进行的是浅比较,重新渲染时,函数的引用是发生改变的,所以会导致子组件重新渲染,而用useCallback后只要依赖的变量未发生改变将始终返回同一个函数引用,不会导致子组件重新渲染。

    注意区别于useMemo 缓存的是函数的返回结果。useCallback缓存的是函数。
    使用案例:

    import React, { useCallback, useState, useEffect } from 'react';
    function Demo_useCallback() {
      const [query, setQuery] = useState(1);
      const [queryOther, setQueryOther] = useState(1);
      const fecthData = useCallback(() => {
        console.log('新的fetch');
        return query;
      }, [query])
      const add = () => {
        console.log('点击add');
        setQuery(query + 1);
      }
      const addOther = () => {
        console.log('点击addOther');
        setQueryOther(queryOther + 1);
      }
      return (
        <div>
          <Child fecthData={fecthData} />
          <button onClick={add}>+1</button>
          <button onClick={addOther}>other+1</button>
          father
          <div>{ query }</div>
        </div>
      );
    }
    const Child = React.memo((props)=>{
      console.log('子组件相关内容');
      useEffect(() => {
        const querN = props.fecthData();
        console.log('子组件调用该函数获取到相关内容', querN);
      }, [props.fecthData])
      return <div>
        Child
      </div>
    }
    )
    export default Demo_useCallback;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    react 常用hooks使用

    React.memo检测的是props中数据的栈地址是否改变。而父组件重新构建的时候,会重新构建父组件中的所有函数(旧函数销毁,新函数创建,等于更新了函数地址),新的函数地址传入到子组件中被props检测到栈地址更新。也就引发了子组件的重新渲染。所以,在上面的代码示例里面,子组件是要被重新渲染的。上文中的fetchData因为失去了useCallback的保护使得子组件的props发生了变化,从而React.memo也失去了作用,而且因为fetchData因为失去了useCallback的保护,使得点击other+1按钮改变无关的变量时,子组件也调用了请求函数。

    React中setState是同步还是异步的?

    React中setState是同步还是异步的?
    代码验证:

    import React from "react";
    class App extends React.Component {
      state = { counte: 0 };
      add = () => {
        this.setState({ counte: this.state.counte + 1 });
        console.log("更新后打印的值:", this.state.counte);
      };
      render() {
        return (
          <div>
            <h2>{this.state.counte}</h2>
            <button onClick={this.add}>增加</button>
          </div>
        );
      }
    }
    export default App;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    React中setState是同步还是异步的?
    如何拿到更新后的值

    this.setState({ counte: this.state.counte + 1 }, () => {
      console.log("更新后的值:", this.state.counte);
    });
    
    • 1
    • 2
    • 3

    函数写法

     this.setState(
      () => ({ counte: this.state.counte + 1 }),
      () => {console.log("更新后的值:", this.state.counte)}
     )
    
    • 1
    • 2
    • 3
    • 4

    React中setState是同步还是异步的?

    完结~

  • 相关阅读:
    Echarts实现半圆形饼图,Echarts实现扇形图
    省HVV初体验(edu)
    四十三、视图层
    去哪儿网2023正式秋招啦,来这里可以内推
    AI辅助研发正在成为造福人类的新生科技力量
    Locust简单使用
    iOS图文混排 头部有图片等相关内容的trick实现方式
    Redis的五种常用数据类型
    Stream手动分页
    猿创征文| Dcoker实战:Linux环境安装Redis图文教程
  • 原文地址:https://blog.csdn.net/qq_42696432/article/details/133943462