React Hooks

React Hooks 是什么?

Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性

React Hooks 有哪些?

1.基础 Hook

  • useState
    定义状态和更改状态
    作用:返回一个 state,以及更新 state 的函数。

useState案例:

import React,{useState} from 'react'

export default function Hello() {
    //    状态    更新状态的方法    定义初始值
    const [count,setCount]=useState(0)
    const [flag,setFlag]=useState(true)
    return (
        <div>
            <h3>计数</h3>
            <button onClick={()=>{setCount(prevCount=>prevCount+1)}}>+</button>//prevCount就是上一次的值
            <p>{count}</p>
            <hr/>
            <h3>开关</h3>
            <button onClick={()=>{setFlag(false)}}>修改状态</button>
            <p>灯泡{flag ? '亮' : '暗'}</p>
        </div>
    )
}
  • useEffect
    1.相当于是componentDidMount / componentDidUpdate / componentWillUnmount 结合
    作用:

    1. 数据请求
    2. 真实dom操作
    3. 清除无用变量和无用事件

    2.写法有三种

useEffect案例:
src/components/Hello.js

import React,{useEffect,useState} from 'react'

export default function Hello() {
    const [count,setCount] = useState(0)
    const [msg,setMsg] = useState('zhang')
    //! 相当于componentDidMount钩子的作用
        useEffect(() => {//初始化定义状态的时候必定会执行
            console.log('useEffect')
            document.querySelector('p').style.background = 'red'
        },[])
    //! componentDidMount + componentDidUpdate + watch 
        useEffect(() => {
            console.log('useEffect')
        },[count,msg])
    //! useEffect后面没有跟第二个参数,那么只要数据改变,它就触发    
        useEffect(() => {
            console.log('useEffect')
        })
    //! componentDidMount + componentWillUnmount
    useEffect(() => {
        return () => {
            console.log('Hello组件被销毁了')
        }
    })
    return (
        <div>
            <p> 123 </p>
            <button onClick={() => {setCount(preCount=>preCount + 1)}}> + </button>
            <p> {count} </p>
            <hr/>
            <button onClick={() => {setMsg('eason')}}>修改msg </button>
            <p> {msg} </p>
        </div>
    )
}

属性的监听应用场景:控制页面的头部、底部是否存在及显示

补充:
useEffect 的回调参数返回的是一个清除副作用的 clean-up 函数。因此无法返回 Promise,更无法使用 async/await
解决方案:
1.再包装一层 async 函数,置于 useEffect 的回调函数中,变相使用 async/await

const fetchAPI =async()=> {
  let response = await fetch('api/data')
  response = await res.json()
  setData(response)
}
 
useEffect(() => {
  fetchAPI();
}, []);

2.useEffect中异步函数采用IIFE写法

useEffect(() => {
    (async () => {
      const { data } = await getStorage('xxx-xxx-xxx');
      if(data){
      console.log(data)
      }
    })();
  }, [type, success]);
  • useContext
    接收一个 context 对象(React.createContext 的返回值)并返回该 context 的当前值
    作用:用于跨组件通信

useContext案例:
src/context/index.js

import { createContext } from 'react'
export const moneyContext = createContext(0)

App.js

import React from 'react'
import Father from './components/Father'
import {moneyContext} from './context'
export default function App() {
  const money = 30000
  return (
    <div>
      <moneyContext.Provider value={money}>
        <Father/>
      </moneyContext.Provider>
    </div>
  )
}

src/components/Father.js

import React from 'react'
import Son from './Son'

export default function Father() {
    return (
        <div>
            <Son/>
        </div>
    )
}

src/components/Son.js

import React,{useContext} from 'react'
import {moneyContext} from '../context'

export default function Son() {
    const money = useContext(moneyContext)
    return (
        <div>
            { money }
        </div>
    )
}

在这里插入图片描述

2.额外的 Hook

  • useRef
    作用:用于获取组件或元素

useRef案例:
目的:在父组件里获取子组件的方法
App.js

import React,{useRef} from 'react'
import Hello from './components/Hello'
//要在App组件里使用Hello组件里的方法
export default function App() {
  const ele=useRef(null)//组件和dom节点都是对象
  const dom=useRef(null)
  function add(){
    console.log('ele',ele)//ele {current: Hello}
    ele.current.fn()
    dom.current.style.background="red"
  }
  return (
    <div>
      <button onClick={add}>触发Hello组件里的方法</button>
      {/* 绑定在组件上 */}
      <Hello ref={ele}/>
      {/* 绑定在元素上 */}
      <p ref={dom}>123</p>
    </div>
  )
}

src/components/Hello.js (子组件Hello组件是类组件的情况,ref绑定在类组件上)

import React, { Component } from 'react'
export default class Hello extends Component {
    fn=()=>{
        alert('useRef')
    }
    render() {
        return (
            <div>
                
            </div>
        )
    }
}

问题:当Hello组件是函数组件时?
控制台警告,建议我们使用React.forwardRef() - (ForwardRef ref绑定函数组件的)
问题:子组件使用了React.forwardRef()也拿不到FnComp组件,怎么办?
因为const fn=useRef(null)绑定的是函数组件,没有键值对,不是对象模型
解决方案:useImperativeHandle

  • useImperativeHandle
    作用:
    useImperativeHandle 可以让你在使用 ref 时自定义暴露给父组件的实例值

useImperativeHandle案例:
App.js

import React,{useRef} from 'react'
import FnComp from './components/FnComp'
//要在App组件里使用Hello组件里的方法
export default function App() {
  const fn=useRef(null)//绑定的是函数组件,没有键值对,不是对象模型
  function add(){
    //子组件是函数组件时
    // console.log('函数组件',fn)// 不使用useImperativeHandle时  fn {current: null}
    fn.current.fn()//使用useImperativeHandle时 可以获取到子组件的方法
  }
  return (
    <div>
      <button onClick={add}>触发FnComp组件里的方法</button>
      <FnComp ref={fn}/>
    </div>
  )
}

src/components/FnComp.js (FnComp组件是函数组件的情况)

import React,{useImperativeHandle} from 'react'

function FnComp(props,ref) {
    useImperativeHandle(ref,()=>{
        return {
            fn(){
                alert('函数组件')
            }
        }
    })
    return (
        <div>
            
        </div>
    )
}
export default React.forwardRef(FnComp)
  • useMemo
    返回一个 memoized 值
    作用:
    用于完成优化,用于做缓存的
    注意:函数组件默认帮我们做好了新旧状态判断的拦截,相当于做了shouldComponentUpdate钩子

useMemo案例:
src/components/Hello.js - 不使用useMemo的情况

import React,{useState} from 'react'

export default function Hello() {
    const [count,setCount]=useState(0)
    const arr=[1,2,3,4]
    function renderArray(){
        console.log('arr渲染了')
        return arr.map((item,index)=><div key={index}>{item}</div>)
    }
    return (
        <div>
            <button onClick={()=>{setCount(preCount=>preCount+1)}}>+</button>
            <p>{count}</p>
            <hr/>
            {renderArray()}
        </div>
    )
}

在这里插入图片描述
问题:点击按钮,改变count数据,renderArray函数有必要重新触发,进行渲染吗?
显然是没有必要的,但是状态改变,组件需要重新渲染,renderArray就会重新触发,对性能很不好
解决方案:使用useMemo对renderArray函数进行包裹一下
src/components/Hello.js - 使用useMemo的情况

import React,{useState,useMemo} from 'react'

export default function Hello() {
    const [count,setCount]=useState(0)
    const arr=[1,2,3,4]
    const renderArray=useMemo(() => {
        console.log('arr渲染了')
        return arr.map((item,index)=><div key={index}>{item}</div>)
    }, [])//当[count]填入count就会有关联
    return (
        <div>
            <button onClick={()=>{setCount(preCount=>preCount+1)}}>+</button>
            <p>{count}</p>
            <hr/>
            {renderArray}
        </div>
    )
}

在这里插入图片描述
改变count时,与count无关的renderArray不会再进行重复的渲染

  • useCallback
    返回一个 memoized 回调函数
    专门用于父子组件的连通

useCallback案例:
组件的状态改变,组件需要重新渲染,子组件是父组件的一部分,所以子组件也会重新渲染
要求:
1. 我需要给子组件传递一个方法
2. count改变时,这个方法触发,val改变时不触发

src/components/Father.js - 不使用useCallback的情况

import React,{useState} from 'react'
import Son from './Son'

export default function Father() {
    const [count,setCount]=useState(0)
    const [val,setVal]=useState('')
    function renderItem(){
        console.log('render')
    }
    return (
        <div>
            <button onClick={()=>{setCount(preCount=>preCount+1)}}>+</button>
            <p>{count}</p>
            <hr/>
            <input type='text' onChange={e=>{setVal(e.target.value)}} defaultValue={val}/>
            {/* 将renderItem函数传给Son组件 */}
            <Son renderItem={renderItem}/>
        </div>
    )
}

在这里插入图片描述
src/components/Father.js - 使用useCallback的情况

import React,{useState,useCallback} from 'react'
import Son from './Son'
export default function Father() {
    const [count,setCount]=useState(0)
    const [val,setVal]=useState('')
    //不使用useCallback的情况
    // function renderItem(){
    //     console.log('render')
    // }
    //----------------------------------------
    //使用useCallback的情况
    const renderItem=useCallback(()=>{
        console.log('render')
    },[count])
    return (
        <div>
            <button onClick={()=>{setCount(preCount=>preCount+1)}}>+</button>
            <p>{count}</p>
            <hr/>
            <input type='text' onChange={e=>{setVal(e.target.value)}} defaultValue={val}/>
            {/* 将renderItem函数传给Son组件 */}
            <Son renderItem={renderItem}/>
        </div>
    )
}

src/components/Son.js

import React from 'react'

function Son(props) {
    const {renderItem}=props
    return (
        <div>
            {renderItem()}
        </div>
    )
}
export default React.memo(Son)//只有需要被渲染的组件被渲染,所以这是一个性能提升的方法

在这里插入图片描述
总结:useMemo 传的是值,useCallback 传的是方法,给的东西不一样但是效果是一样的,都是来做优化的

  • useLayoutEffect
    其函数签名与 useEffect 相同,但它会在所有的 DOM 变更之后同步调用 effect。也就是说它是最大的,管所有的DOM节点,写在index.js文件中,项目中可用可不用

  • useDebugValue
    useDebugValue 可用于在 React 开发者工具中显示自定义 hook 的标签
    自定义 Hook 是一个函数,其名称以 “use” 开头,函数内部可以调用其他的 Hook。
    自定义Hook核心作用是用来复用逻辑的,相当与类组件中的高阶组件,就是一个封装函数,把公用逻辑放到封装函数里去了,谁调用自定义Hook,谁就有这个公用的逻辑了

useDebugValue案例:
src/hooks/index.js

//! 自定义封装Hooks 
//! Hooks就是一个函数而已
//! 逻辑复用
import React,{useDebugValue,useState} from 'react'
export function useStatus(num){
    const [count,setCount]=useState(0)
    useDebugValue(count ? 1 : 0)//里面跟一个状态
    if(num!==0){
        return '不为0'
    }else{
        return '为0'
    }
}

App.js

import React from 'react'
import {useStatus} from './hooks'
export default function App() {
  const msg=useStatus(0)
  return (
    <div>
      {msg}
    </div>
  )
}

在这里插入图片描述

  • useReducer
    与 useState 的区别:
    1.当 state 状态值结构比较复杂时,使用 useReducer 更有优势。
    2.使用 useState 获取的 setState 方法更新数据时是异步的;而使用 useReducer 获取的 dispatch 方法更新数据是同步的。
import { useReducer } from '@alipay/bigfish/react';

const initState: any = {
  tableData: [],
  pageTotal: 0,
};

const reducer = (state: any, action: any) => {
  const { type, payload } = action;
  switch (type) {
    case 'setState':
      return { ...state, ...payload };
    default:
      return state;
  }
};

export default function billManagementState() {
  const [state, dispatch] = useReducer(reducer, initState);
  return { state, dispatch };
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值