概念+示例+横向对比+难点解析征服八大react hooks

本文详细介绍了React的八大核心Hooks,包括useState、useReducer、useEffect、useLayoutEffect、useMemo、useCallback、useRef和useContext。通过实例展示了它们各自的概念、使用场景和关键区别,帮助开发者更好地理解和掌握ReactHooks的使用,优化状态管理和组件性能。
摘要由CSDN通过智能技术生成

8大hooks概念、使用场景

前言

对不同阶段的react开发者会有不同的效果,最终目的是能够对8大react hooks,完全理解,游刃有余。对比useState和useReducer,什么时候使用useMemo和useCallback,useEffect的参数… …

useState

概念

定义类组件的状态

相关问题
为什么usestate是数组

如果 useState 返回的是数组,那么使用者可以对数组中的元素命名,代码看起来也比较干净 如果 useState 返回的是对象,在解构对象的时候必须要和 useState 内部实现返回的对象同名,想要使用多次的话,必须得设置别名才能使用返回值

useEffect

概念
  • 副作用hooks
  • 给没有生命周期的组件,添加结束渲染的信号
  • 执行时机:在渲染结束之后执行
  • 不管什么场景下,useEffect都会在初次渲染期间执行
示例
import React,{ useEffect, useState } from 'react'

function StateFunction () {
	const [num, setNum] = useState(0)
	useEffect( () => {
	    console.log('函数式组件结束渲染')
	    const updateMouse = (e) => {
	        console.log('打印当前位置')
	        setPositions({ x:e.clientX, y:e.clientY })
	    }
	    document.addEventListener('click',updateMouse) //  添加绑定方法事件(要修改依赖,绑定到依赖上)
	
	    return () => {
	        //  在每次执行useEffect之前都会执行上一次return中内容
	        document.removeEventListener('click',updateMouse)
	        //  移除绑定方法事件(要修改依赖,绑定到依赖上)
	        console.log('1111销毁')
	    }
	})
}

1.第一个参数,接收一个函数作为参数
2.第二个参数,接收【依赖列表】,只有依赖更新时,才会执行函数
3.返回一个函数,先执行返回函数,再执行参数函数

useLayoutEffect

概念

一般将useLayoutEffect称为有DOM操作的副作用hooks。作用是在DOM更新完成之后执行某个操作。执行时机:在DOM更新之后执行。
与eusEffect不同点:执行示例代码会发现useLayoutEffect永远比useEffect先执行,这是因为DOM更新之后,渲染才结束或者渲染还会结束。

useMemo

概念

传递一个创建函数和依赖项,创建函数会需要返回一个值,只有在依赖项发生改变的时候,才会重新调用此函数,返回一个新的值

useCallback

概念

父组件的传入函数不更新,就不会触发子组件的函数重新执行

示例
function Parent () {

    const [num, setNum] = useState(1)
    const [age, setAge] = useState(18)

    const getDoubleNum = useCallback( () => {
        console.log(`获取双倍Num${num}`)
        return 2 * num
    },[num] )

    return (
        <div onClick={ () => {setNum( num => num+1 )} }>
            这是一个函数式组件————num:{  getDoubleNum() }
            <br></br>
            age的值为————age:{ age }
            <br></br>
            set.size:{set.size}
            <Child callback={ getDoubleNum() }></Child>
        </div>
    )
}

function Child(props) {
    useEffect( () => {
        console.log('callback更新了') //这里代表的是需要跟随传入内容的改变而同步进行的操作
    },[props.callback])

    return (
        <div>
            子组件的getDoubleNum{props.callback}
        </div>
    )
}
总结

在子组件不需要父组件的值和函数的情况下,只需要使用memo函数包裹子组件即可
如果有函数传递给子组件,使用useCallback
缓存一个组件内的复杂计算逻辑需要返回值时,使用useMemo

useRef

概念

返回一个子元素索引,此索引在整个生命周期中保持不变。
作用也就是:长久保存数据。
注意事项,保存的对象发生改变,不通知。属性变更不会重新渲染

示例
const [num, setNum] = useState(0)

const ref = useRef()
useEffect( () => {
    ref.current = setInterval( () => {
        setNum( num => num+1 )
    },400 )
    // ref.current = '111'
},[] )

useEffect( () => {
    if(num > 10){
        console.log('大于10了,清除定时器')
        console.log('ref.current',ref.current)
        clearTimeout(ref.current)
    }
},[num] )

return (
    <div>
        这是一个函数式组件————num:{  num }
    </div>
)

useContext

概念

useContext是让子组件之间共享父组件传入的状态的,特别是推荐应用程序需要在比两层更深的组件之间共享状态时使用,或者父组件有传入一个值到多个不同的子组件中。

示例
一个值到多个的使用前
function StateFunction () {
    const [num, setNum] = useState(1)

    return (
        <div>
            <button onClick={ ()=> setNum(num => num+1) }>增加num的值+1</button>
            <br></br>
            这是一个函数式组件——num:{  num }
            <Item1 num={num}></Item1>
            <Item2 num={num}></Item2>
            //	......
        </div>
    )
}

function Item1 (props) {
    return (
        <div>
            子组件1 num:{ props.num }
        </div>
    )
}

function Item2 (props) {
    return (
        <div>
            子组件2 num:{ props.num }
        </div>
    )
}

一个值到多个使用后
const Context = createContext(null)

function StateFunction () {
    const [num, setNum] = useState(1)

    return (
        <div>
            <button onClick={ ()=> setNum(num => num+1) }>增加num的值+1</button>
            <br></br>
            这是一个函数式组件——num:{  num }
            <Context.Provider value={num}>
                <Item3></Item3>
                <Item4></Item4>
            </Context.Provider>
        </div>
    )
}

function Item3 () {
    const num = useContext(Context)

    return (
        <div>
            子组件3: { num }
        </div>
    )
}

function Item4 () {
    const num = useContext(Context)

    return (
        <div>
            子组件4: { num+2 }
        </div>
    )
}
一传多优化总结

使用useContext优化后,代码如下,这样我们只需要在子组件中使用useContext(Context句柄)来获取数据即可,添加同类子组件时不需要再关注父组件中子组件定义时的props传入值,使用方法如下

需要引入useContetx,createContext两个内容
通过createContext创建一个context句柄
Context.Provider来确定数据共享范围
通过value来分发内容
在子组件中,通过useContext(Context句柄)来获取数据
注意事项,上层数据发生改变,肯定会触发重新渲染(点击button按钮触发父组件更新传入的num值能看到子组件重新渲染)

两层以上子组件传递
const ContextA = createContext(null);

const Parent = () => {
  const [stateA, dispatchA] = useReducer(reducerA, initialStateA);
  const valueA = useMemo(() => [stateA, dispatchA], [stateA]);
  return (
    <ContextA.Provider value={valueA}>
      <Child1 />
    </ContextA.Provider>
  );
};

const Child1 = () => (
  <GrandChild1 />
);

const GrandChild1 = () => (
  <GrandGrandChild1 />
);

const GrandGrandChild1 = () => {
  const [stateA, dispatchA] = useContext(ContextA);
  return (
    ...
  );
};
多个值的Context

🙅👇
在这里插入图片描述
🙆下面
推荐使用这个开源库【 react-hooks-global-state】

import { createGlobalState } from 'react-hooks-global-state';

const initialState = { 
  a: ...,
  b: ...,
  c: ...,
};
const { GlobalStateProvider, useGlobalState } = createGlobalState(initialState);

const App = () => (
  <GlobalStateProvider>
    ...
  </GlobalStateProvider>
);

const Component1 = () => {
  const [valueA, updateA] = useGlobalState('a');
  return (
    ...
  );
};
Q: 如何跨文件使用Context这个变量

A: 把这个Ctx 直接暴露出去 即 export const Ctx = createContext();
伪代码:

// ParentComponent
export let Context = createContext(null)
const ParentComponent = ()=>{
let [num, setNum] = useState(1)
return (
		<div>
			<Context.Provider value={{num,setNum}}
				<grandChild show={True}>
			</Context.Provider >
			<button onClick=()=>setNum(num + 1)>+1</button>
	   </div>

		)	
}
export default ParentComponent;

// grandChildComponent

import {Context} form './ParentComponent'
const grandChildComponent = ()=>{
	return (
		<Context.Consumer>
			{
				value=>(
					<div>
						<span>{value.num}</span>
						<button onClick={()=>value.setNum(value.num+1)}>+1</button>
					</div>
				)
			}
			
		</Context.Consumer>
	)
}
export default grandChildComponent;

具体写法可以参考隔壁

useReducer

概念

useReducer通过向下传递分派(而不是回调)让子组件之间共享父组件传入的状态。涉及多个子值的复杂状态逻辑或下一个状态依赖于前一个状态时,可以优化触发深度更新的组件的性能。
使用useReducerwhich 返回一个dispatch在重新渲染之间不会改变的方法,并且您可以在 reducer 中拥有操作逻辑。

示例
const store = {
    age:18,
    num:1
}	//	数据仓库

const reducer = (state, action) => {
    switch(action.type){
        case 'add':
            return {
                ...state,
                num: action.num+1
            }

        default:
            return {
                ...state
            }
    }
} //	管理者

function StateFunction () {
    const [state,dispacth] = useReducer(reducer,store)  //	①

    return (
        <div>
            <button onClick={ () => {
                dispacth({
                    type: 'add',
                    num: state.num
                })
            } }>
                增加num的值+1
            </button>
            <br></br>
            这是一个函数式组件——num:{  state.num }
        </div>
    )
}

即是:
创建数据仓库store(状态)和管理者reducer(动作),定义一个数组获取状态和改变状态的动作,触发动作的时候需要传入type类型判断要触发reducer哪个动作,然后进行数据的修改。需要注意的地方是,在reducer中return的对象中,需要将state解构,否则状态就剩下一个num值了

与useState的区别

在这里插入图片描述

引用

理解和引用的传送门👇

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值