我在React企业级项目开发(ToB业务)中用过的React Hooks

240 阅读5分钟

一、useState(更新状态量)

  • 使用场景:函数组件更新状态

  • 写法:const [state,setState]=useState(initialState)

  • initialState代表state的初始状态;也可以写成()=>{}

  • setState一般放在事件处理函数handleXXX(){ }useEffect()中;有两种写法: a.setState(newState) b. setState(pre=>{return })


二、useEffect(副作用)

  • 使用场景:用于处理组件中的effect,通常用于请求数据,事件处理,订阅等相关操作

  • 写法:

import { useEffect } from 'react'
useEffect(() => {
  // 副作用函数的内容
  ...
  return ()=>{ /* 做清理工作*/ }  // 清除函数,可写可不写
}, [depency])
  • return函数在组件销毁前执行,模拟类组件生命周期的 WillUnmount

  • useLayoutEffect在浏览器渲染前执行,useEffect在浏览器渲染完成后执行

  • 第二个参数数组中写依赖参数,数组不写参数表示函数组件初始化时只执行一次,不写数组表示函数组件的初始化和每次更新都会执行


三、useRef(返回一个可变的ref对象)

  • 使用场景:获取DOM元素、多次渲染之间共享数据

  • 写法:const xxx = useRef(initialValue)

  • initialValue被赋初始值给其返回值的.current对象,.current对象保存着可变值;

  • ref对象与自建一个对象的区别是:useRef会在每次渲染时返回同一个ref对象,即返回的ref对象在组件的整个生命周期内保持不变。而自建对象每次渲染时都建立一个新的

  • 挂载dom节点<div ref={xxx}></div>,.current对象就拥有了dom元素的属性,常用的如失焦聚焦

  • 挂载子组件<Child ref={xxx} /> ,可以在子组件函数搭配forwradRef((props,ref)=>{})使用useImperativeHandle(ref,createHandle,[deps])来获取子组件的值

import { useRef } from 'react'
//父组件
const Parent=()=>{
const childRef=useRef(null)
console.log(childRef.current.getData())
return <Child ref={childRef} />
}
//子组件
const Child = forwardRef((props, ref) => {
const data=[1,2,3]
useImperativeHandle(ref, () => ({ getData, }));
const getData = () => {return data};
return <div>子组件</div>; })

四、useContext(上下文)

  • 使用场景:跨组件传值

  • 写法:

//在上级组件导入并调用createContext方法,得到Context对象,并导出
import { createContext } from 'react'
export const Context = createContext()

//在上级组件的jsx元素里用Context.Provider包裹需要接收数据的后代组件
const Parent=()=>{
const data='需要传递的数据';
const setData=()=>{};
const {Provider}=Context;
 return (
 <>
 <div>父组件</div>
 <Provider value={data,setData}>
  <Child />
 </Provider>
 </>
 )
}
//在需要获取公共数据的后代组件中,导入useContext,并按需获取需要的value
import React, { useContext } from 'react'
import { Context } from './index'
const Child = () => {
    const {data,setData} = useContext(Context) // 这里的公共数据就是根组件value的值
    return <div onClick={setData}>{data}</div>
}

五、useMemo 和 useCallback (解决使用React Hooks产生的无用渲染的性能问题)

  • 使用场景:缓存计算结果(前者缓存计算结果的值,即避免在每次渲染时都进行高开销的计算;后者缓存函数,因为函数式组件每次任何⼀个 state 的变化整个组件都会被重新刷新,⼀些函数是没有必要被重新刷新的,此时就应该缓存起来,提⾼性能,减少资源浪费)

  • 写法: useMemo:let memo =useMemo(()=>{return xxx},[depency])

useCallback:const callback = useCallback(()=>{return ()=>{}},[depency])

import React, { useState, useCallback,useEffect, memo } from 'react';
const Parent = () =>{
    const [count, setCount] = useState(1);
    const [val, setVal] = useState('');
    
// 借助useCallback来返回函数,然后把这个函数作为props传递给子组件;这样,子组件就能避免不必要的更新
// 当count改变时才重新渲染callback,如果第二个参数传空数组,后续渲染只会记住第一次
    const callback = useCallback(() =>{setCount(pre=>pre+1)} , []); 
    
// 不缓存,每次 count 更新时都会重新创建
    const add=()=>{setCount(pre=>pre+1)}
    return (<>
        <h4>父组件:{count}</h4>
        <Child add={add} callback={callback} val={val}/>
        <div>
            <input value={val} onChange={e => setVal(e.target.value)}/>
        </div>
    </>)
}

//组件使用memo包裹可以减少渲染;如果子组件的pros没有发生变化,则不会重新渲染子组件
const Child= memo(({add, callback,val}) =>{
//只有当父组件的val改变时,才会触发更新
    let memo=useMemo(()=>{
    console.log('子组件更新新了');
    return Math.floor(Math.random()*3)
    },[val])
 
//没有缓存,由于每次都创建,memo 认为两次地址都不同,属于不同的函数,所以会触发 useEffect
    useEffect(()=>{console.log('add')},[add]);

// 有缓存,memo 判定两次地址都相同,所以不触发 useEffect
    useEffect(()=>{console.log('callback')},[callback]);
    return <>
       <h4>{memo}</h4>
       <button onClick={add}>+1,不使用callback</button>
       <br>
       <button onClick={callback}>+1,使用callback</button>
    </>
})
  • 如果用函数或变量作为 props 传给子组件,请一定要用useMemo 和 useCallback,避免子组件的非必要渲染

六、useReducer(useState的代替方案,用于 state 复杂变化)

  • 使用场景:单个组件状态管理,提供类似 Redux 的功能

  • 写法:const[state,dispatch]=useReducer(reducer,initialState)

  • reducer 接受两个参数state和action ,然后返回一个状态 state 和 dispath,state 是返回状态中的值,而 dispatch 是一个可以发布事件来更新 state的函数 ,disptachd的参数对象属性通常为type和payload

import React, {useReducer} from 'react';
const initialState=0;
const reducer=(state,action)=>{
const { type, payload } = action;
switch(type){
  case:'a':
   return {
        ...state,
        ...payload
      };
  default:
    break;
}
export default ()=>{ 
 const[state,dispatch]=useReducer(reducer,initialState);
 const handleClick = () => { const data = [1,2,3];dispatch({ type:'a',  payload:data }) };
 return <div onClick={handleClick}>123</div> 
 }

七、useDispatch(组件调用dispatch需要useDispatch来创建) 和 useSeletor (组件获取redux的状态)

  • 使用场景:前者共享状态,返回Redux的store中对dispatch的引用,可执行redux中的方法;后者获取状态,返回Redux的store中提取state

  • 写法: useDispatch:const dispatch =useDispatch()

useSeletor:const {stateA,stateB...} = useSelector((state: any) => state['xxx'])

import { useDispatch,useSeletor} from 'react-redux'
export default ()=>{
const {stateA,stateB} = useSelector((state: any) => state['xxx']); //store里面对应的namespace名称
const dispatch =useDispatch();
const handleClick = () => {
   const data = [1,2,3]
   dispatch({
   type:'xxx/xxx', //type里面写store里面对应的namespace名称然后对应的reducer名称
   payload:data
   })
};
return <div onClick={handleClick}>{stateA}-{stateB}</div>
}