React Hooks

65 阅读6分钟

Hooks

  • hooks:react为函数式组件提供的一些方法,包含动态数据,生命周期等等

  • hooks:只能写在 组件自定义hooks

  • 按需引入:

    import { useState,useEffect,useContext,useReducer,useRef,useLayoutEffect,useMemo,useCallback } from "react"
    

基础HOOK

⭐useState

  • 作用:为函数组件提供动态数据,更新视图

  • 语法1:

    let [默认数据变量,修改数据的方法]  = useState('默认数据')
    修改数据方法(新数据)
    🌰:
    let [name,setName] = useState('慢羊羊')
    setName('懒羊羊')
    
    • 基本数据类型

      function App() {
          let [age, setAge] = useState(12)
          const changeAge = () => {
              setAge(age + 1)
          }
          return (
              <div>
                  <h3>喜羊羊{age}岁啦</h3>
                  <button onClick={() => changeAge()}>新年好</button>
              </div>
          );
      }
      
  • 语法2:

    let [默认数据变量,修改数据的方法]  = useState('默认数据')
    数组:
    	修改数据的方法((state)=>{return[...新数据]})
    	`形参state为默认数据`
    对象:
    	修改数据的方法((state)=>{自行合并新旧数据})
    	`形参state为默认数据`
    
    • 复杂数据类型--数组

      //🌰:添加
      function App() {
          let [list, setList] = useState(['🍉', '🍑'])
          const changeList = () => {
              let item = '🍌'
              setList((state) => {
                  state.push(item)
                  return [...state]
              })
          }
          return (
              <div>
                  {
                      list.map((item, index) => {
                          return <div key={index}>{item}</div>
                      })
                  }
                  <button onClick={() => changeList()}>添加</button>
              </div>
          );
      }
      
      // 🌰:删除
      function App() {
          let [list, setList] = useState(['🍊', '🍉', '🍑', '🍌', '🍎'])
          const delList = (index) => {
              setList((state) => {
                  state.splice(index, 1)
                  console.log(state);
                  return [...state] // 改变引用地址
              })
          }
          return (
              <div>
                  {
                      list.map((item, index) => {
                          return <div key={index}>{item}<button onClick={() => delList(index)}>删除</button></div>
                      })
                  }
              </div>
          );
      }
      
    • 复杂数据类型--对象

      🌰:
      function App() {
          let [obj, setObj] = useState({ name: '小灰灰', age: 6 })
          const changeAge = () => {
              setObj((state) => {
                  return { ...state, age: state.age + 1 }
              })
          }
          return (
              <div>
                  <h5>{obj.name}今年{obj.age}啦</h5>
                  <button onClick={() => changeAge()}>过年了</button>
              </div>
          );
      }
      

react更新机制

  • react更新机制:重新创建组件

  • 问题:

    在项目中,有时需要局部更新,其他地方不更新
    可以使用useEffect来解决,详情见下文
    

⭐useEffect

  • useEffect:本质是一个 宏任务

  • 副作用:

    • 当做组件的生命周期(相当于**mounted**)
    • 组件的销毁
    • 监听
  • 基本语法:想要组件加载和更新(工作中不用

    function App() {
        let [num, setNum] = useState(0)
        useEffect(() => {
            console.log('组件加载完毕,组件更新');
        })
        const changeNum = () => {
            setNum(num + 1)
        }
        return (
            <div>
                <h5>{num}</h5>
                <button onClick={() => changeNum()}>过年了</button>
            </div>
        );
    }
    
  • 当做组件的生命周期使用

    • 语法:useEffect(处理函数,依赖项)

      function App() {
          let [num, setNum] = useState(0)
          useEffect(() => {
              console.log('组件加载完毕,组件更新');
          }, [])
          const changeNum = () => {
              setNum(num + 1)
          }
          return (
              <div>
                  <h5>{num}</h5>
                  <button onClick={() => changeNum()}>过年了</button>
              </div>
          );
      }
      
    • 如果依赖项为空数组,那么处理函数只会执行一次,相当于生命周期 mounted

    • 如果没有依赖项,只要组件函数重新执行,处理函数就会重新执行

    • 使用场景:DOM操作、发送请求...

  • 组件的销毁

    • 语法:useEffect 的返回值返回一个方法

    • 使用场景:清除定时器...

      function App() {
          let [num, setNum] = useState(0)
          const addNum = () => {
              setNum(num+1)
          }
          return (
              <div>
                  {num % 3 == 0 ? <Son></Son> : ''}
                  <button onClick={() => addNum()}>点击</button>
              </div>
      )
      }
      // 子组件
      function Son() {
          // 定时器
          let time = setInterval(() => {
              console.log('定时器');
          }, 1000)
          useEffect(() => {
              return () => {
                  console.log('子组件销毁了')
                  clearInterval(time)
              }
          })
          return (
              <div>
                  <h3>Son</h3>
              </div>
          )
      }
      
  • 监听

    • 语法:useEffect(()=>{},[监听的数据1,监听的数据2]...)

    • 相当于 vue 中的 watch

    • 默认立即执行

      function App() {
          let [age, setAge] = useState(12)
          let [name, setName] = useState('喜羊羊')
          useEffect(() => {
              console.log('监听', age);
          }, [age])
          return (
              <div>
                  <h3>{name}--{age}</h3>
                  <button onClick={() => setName('美羊羊')}>姓名</button>
                  <button onClick={() => setAge(age + 1)}>年龄</button>
              </div>
          )
      }
      

⭐useContext

  • 提供上下文,本质是一个全局变量

  • 在最外层提供数据,在里面可以获取到数据

  • 使用场景:全局设置主体、字体...

  • 使用

    1.引入react、useContext
    2.通过React.createContext创建一个数据,返回一个对象(例:Context),使用对象的Provider属性来提供数据
    

3.定义要传递的数据 4.在父级组件中使用<Context.Provider />组件结合value属性来提供数据 <Context.Provider value={数据}></Context.Provider> 5.子组件中通过useContext(Context)获取到父级组件提供的数据


```react
// 1.引入
import React, { useContext } from 'react'
// 2.创建一个数据
let Context = React.createContext() //返回一个对象
console.log(Context);
// 3.定义要传递的数据
let theme = {
  bgcol: 'red'
}
// 父组件
function App() {
  return (
      // 4.父级组件使用
      <Context.Provider value={theme}>
          <div>
              <Son></Son>
          </div>
      </Context.Provider>
  )
}
// 子组件
function Son() {
  // 5.获取父组件提供的数据
  let data = useContext(Context)
  console.log(data);
  return (
      <div style={{ background: data.bgcol }}>子组件</div>
  )
}

额外HOOK

⭐useReducer

  • 用法和 useState 一样,都事用来处理动态数据的

  • 作用:对动态数据的行为进行约束

  • 语法:

    let [默认数据,触发reducer方法] = useReducer(定义的reducer,默认数据)
    
  • reducer是什么?

    1.reducer是一个函数
    2.reducer函数有2个参数(形参)
    	参数1:默认数据
        参数2:触发reducer方法传入的实参(触发reducer方法`dispatch`传递的参数,一个对象)
    3.reducer返回值为最新的数据
    4.只能处理同步问题(相当于vuex中的mutations)
    
// 引入
import { useReducer } from "react";
// 定义reducer
function reducer(state, actions) {
    // 逻辑代码
    switch (actions.type) {
        case '++':
            return state + actions.money;
        case '--':
            return state - 200;
        default:
            return state;
    }
}
function App() {
    // 使用useReducer
    let [moneys, dispatch] = useReducer(reducer, 1000)
    return (
        <div>
            <h3>余额:¥{moneys}.00</h3>
            {/* 通过dispatch触发,传递参数,使用对象 */}
            <button onClick={() => {dispatch( {type:'++',money: 500})} }>收红包</button>
            <button onClick={() => {dispatch( {type:'--',money:200})} }>发红包</button>
        </div>
    );
}

⭐useRef

  • 作用:获取到元素的真实dom

  • 语法:

    let dom1 = useRef(null)
    <元素 ref={dom1} />
    
  • 该组件创建的流程

    1.组件创建完毕,初始化组件的属性
    2.将组件(模板语法jsx) => vnode
    	有事件集中处理,有属性进行绑定
    3.将vnode => 真实dom
    4.做dom操作
    总结:在mouted生命周期中可以拿到真实dom
    
// 引入
import { useRef, useEffect } from "react";
function App() {
    // created生命周期
    let dom1 = useRef(null) //null
    let dom2 = useRef(null) //null
    let num = 100
    // mouted生命周期
    useEffect(() => {
        console.log(dom1); //获取真实dom1
        console.log(document.getElementById('box'));
        console.log(dom2); //获取真实dom2
        console.log(document.getElementById('box2'));
    }, [])
    return (
        <div>
            <h2 id="box" data-id={num} ref={dom1}>DOM-1</h2>
            <h2 id="box2" data-id={num + 10} ref={dom2}>DOM-2</h2>
        </div>
    )
}

useLayoutEffect

  • 语法:与 useEffect 一致

  • 原理:底层代码是 微任务

  • 作用:

    当做生命周期使用
        1.加载之前
        2.更新之前
        3.销毁之前
    监听
    	当被监听的数据发生改变之前执行这个处理方法
    
  • useEffectuseLayoutEffect 的区别

    #*****
    他们俩看起来非常相似,事实上他们只有一点区别:
    `useEffect`:会将渲染的内容更新到dom上,把该显示的都显示了之后,再执行这个回调函数,不会阻塞dom的更新
    `useLayoutEffect`:会在dom渲染的内容更新到dom上之前执行。我已经返回了我要显示的东西,你应该显示到dom上了,但是你还没显示到dom上的时候,你先把这个回调函数执行完,再去吧数据更新到dom上。会阻塞dom的更新
    
// 引入
import { useLayoutEffect, useEffect, useState } from "react";
function App() {
    let [num, setNum] = useState(10)
    useEffect(() => {
        console.log('加载完毕,mounted;', num);
    }, [num])
    useLayoutEffect(() => {
        console.log('挂载前,解析dom前,beforeMount;');
    }, [num])
    return (
        <div>
            {num}
            <button onClick={() => setNum(num + 1)}>++</button>
        </div>
    )
}

组件的渲染

1.组件的渲染:先渲染父组件再渲染子组件
2.性能问题:
	如果在父组件中更新数据,组件会重新渲染,父组件的嵌套组件(子组件)也会被重新渲染
解决方法:
    1、模块化开发,一个功能一个模块(推荐)
    2.使用useMemo、useCallback进行缓存(工作中不用,`面试要讲`)

useMemo

  • 作用:缓存变量

  • 使用场景:此方法比创建变量更消耗性能,项目中不用,面试要说

  • 语法:

    let 缓存变量 = useMemo(()=>{},[监听数据,...])
    
// 引入
import { useMemo, useState } from "react";
function App() {
    let [age, setAge] = useState(12)
    let KeepName = useMemo(() => {
        console.log('更新');
        return '电击小子' // 缓存数据
    }, [])
    return (
        <div>
            <h4>{KeepName} 今年 {age} 岁啦</h4>
            <button onClick={() => setAge(age + 1)}>过年啦</button>
        </div>
    )
}

useCallback

  • 作用:将方法缓存

  • 使用场景:此方法比创建变量更消耗性能,项目中不用,面试要说

  • 语法:

    let 方法名 = useCallback(()=>{ },[监听数据])
    
// 引入
import { useCallback, useState } from "react";
function App() {
    let [num, setNum] = useState(12)
    // 方法
    let fn = () => {
        console.log('执行');
    }
    // 将方法缓存
    let KeepFn = useCallback(() => {
        return fn()
    }, [num])
    KeepFn() // 启用这个缓存方法
    return (
        <div>
            <h3>{num}</h3>
            <button onClick={() => setNum(num + 1)}>修改</button>
        </div>
    )
}

useImperativeHandle

useDebugValue

自定义hooks

  • 自定义hooks

    1.自定义hooks是一个方法(函数)
    2.该函数名以`use`开头
    3.在该函数中可以使用`react`提供的`api`
    
  • 何时写自定义hooks?

    react项目中的方法都可以写成自定义hooks