React

该掌握的八个React Hooks

1.useState

在函数组件中,可以使用useState来定义函数组件的状态。使用useState来创建状态

  • 1.引入
  • 2.接收一个参数作为初始值
  • 3.返回一个数组,第一个值为状态,第二个值为改变状态的函数
        代码如下:
import React,{ useState } from 'react'

function StateFunction () {
    const [name, setName] = useState('函数')
    //     类名,修改函数名            初始值

    return (
        <div onClick={ () => setName('我使用hooks变成这样了') }>
        //	setName也可以写入方法,如setName( val => val+'xxxx' )
            这是一个函数式组件————{name}
        </div>
    )
}

export default StateFunction

2.useEffect

useEffect又称副作用hooks。作用:给没有生命周期的组件,添加结束渲染的信号。执行时机:在渲染结束之后执行

  • 什么是副作用?

    • 副作用 ( side effect ): 数据获取,数据订阅,以及手动更改 React 组件中的 DOM 都属于副作用
    • 因为我们渲染出的页面都是静态的,任何在其之后的操作都会对他产生影响,所以称之为副作用
  • 使用:

    • 1.第一个参数,接收一个函数作为参数
    • 2.第二个参数,接收【依赖列表】,只有依赖更新时,才会执行函数
    • 3.返回一个函数,先执行返回函数,再执行参数函数 
              代码如下:
import React,{ useEffect, useState } from 'react'

function StateFunction () {
    const [num, setNum] = useState(0)
    const [val,setVal] = useState(0)
   //不带参数
    useEffect( () => {
        console.log('2222函数式组件结束渲染')
    })
    //带空参数
    useEffect( () => {
        console.log('2222函数式组件结束渲染')
    },[])
    //带参数的
    useEffect( () => {
        console.log('2222函数式组件结束渲染')
    },[参数])
    //带两个参数
    useEffect( () => {
         console.log('2222函数式组件结束渲染')
    },[num,val])
    return (
        <div onClick={ () => setNum( num => num+1 ) }>
            这是一个函数式组件————{num}
            这是一个函数式组件————{val}
        </div>
    )
}

3.useLayoutEffect

一般将useLayoutEffect称为有DOM操作的副作用hooks。作用是在DOM更新完成之后执行某个操作。执行时机:在DOM更新之后执行

useEffect对比

  • 相同点
    • 1.第一个参数,接收一个函数作为参数
    • 2.第二个参数,接收【依赖列表】,只有依赖更新时,才会执行函数
    • 3.返回一个函数,先执行返回函数,再执行参数函数
    • (所以说执行过程的流程是一样的)
  • 不同点
    • 执行时机不同。useLayoutEffectDOM更新之后执行;useEffectrender渲染结束后执行。执行示例代码会发现useLayoutEffect永远比useEffect先执行,这是因为DOM更新之后,渲染才结束或者渲染还会结束
          代码如下:
const [num, setNum] = useState(0)
//在类组件中用componentWillMount生命周期来实现
useLayoutEffect( () => {
    console.log('useLayoutEfffect')
	//	也可以在此进行事件绑定
    return () => {
    	//	也可以在此进行事件绑定移除
        console.log(1)
    }
},[num])

useEffect( () => {
    console.log('useEffect')
},[num])

return (
    <div onClick={ () => setNum( num => num+1 ) }>
        这是一个函数式组件————{num}
    </div>
)

4.useMemo

使用useMemo可以传递一个创建函数和依赖项,创建函数会需要返回一个值,只有在依赖项发生改变的时候,才会重新调用此函数,返回一个新的值。简单来说,作用是让组件中的函数跟随状态更新(即优化函数组件中的功能函数)。

  • 使用:
    • 1.接收一个函数作为参数
    • 2.同样接收第二个参数作为依赖列表(可以与useEffect、useLayoutEffect进行对比学习)
    • 3.返回的是一个值。返回值可以是任何,函数、对象等都可以
  代码如下:
const [num, setNum] = useState(1)
const [age, setAge] = useState(18)

function getDoubleNum () {
    console.log(`获取双倍Num${num}`)
    return 2 * num  //	假设为复杂计算逻辑
}

return (
  <div onClick={ () => { setAge( age => age+1 )} }>
      <br></br>
      这是一个函数式组件————{  getDoubleNum() }
      <br></br>
      age的值为————{ age }
      <br></br>
  </div>
)

5.useCallback

useCallback缓存的是一个函数,可以对父子组件传参渲染的问题进行优化。简单来说就是,父组件的传入函数不更新,就不会触发子组件的函数重新执行

例如示例代码,我们将getDoubleNum传入子组件,此时点击div区域改变的是num的值,我们使用父组件useCallback配合子组件的useEffect来优化,只有当父组件的num改变导致传入子组件的getDoubleNum改变的时候,我们才会执行子组件某些需要更新的操作(即注释标注处代码),这样就可以避免子组件一些没必要的更新操作反复执行而影响页面性能

  代码如下:
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
  • 如果有值传递给子组件,使用useMemo 参考详解文章:
  • 上述是我个人结合查阅资料的一些理解,如果你觉得我讲的还是不太清楚,可以参考下这两篇写得不错的文章,或许会更清晰

6.useRef

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

  • 未使用useRef,如果我们有这样一个需求如下,需要当某个定时器自增的值达到限制条件后就清除该定时器,如下代码。此时以下的代码其实是没有办法完成给出的需求的,当num大于10后,会发现不停的打印大于10了,清除定时器,而其实是定时器没有清除掉的,所以会一直执行这两个打印内容,但是会发现打印出来的timer显示undefined,这是为什么呢?因为我们每次渲染都是通过setInterval重新返回的timertimer也在更新,也就丢失了timer这个数据,导致无法准确清除某个需要清除的定时器
  代码如下:
const [num, setNum] = useState(0)

let timer
useEffect( () => {
    timer = setInterval( () => {
        setNum( num => num+1 )
    },400 )
},[] )

useEffect( () => {
    if(num > 10){
        console.log('大于10了,清除定时器')
        console.log('timer:',timer)
        //  因为每一个timer都是独立render的,所以获取不到
        clearTimeout(timer)
    }
},[num] )

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

使用useRef后,代码如下。我们可以看到num自增到11后就打印了一次大于10了,清除定时器以及ref.current 1,然后就停止自增了,因为定时器被清除了。ref是一个对象,ref.current存储了该定时器在整个生命周期中的id值,所以当清除定时器的时候,可以准确清除这个定时器

  • 保存一个值,在整个生命周期中维持不变
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>
)
  • 重新赋值ref.current不会主动触发页面重新渲染。当我们将代码修改成下面这样,会在控制台打印发现ref.current的值打印为111,但是页面视图上显示的还是空,这是因为ref保存的对象发生改变,不会主动通知,属性变更不会重新渲染
const [num, setNum] = useState(0)

const ref = useRef()
useEffect( () => {
    ref.current = '111'
    console.log('ref.current',ref.current)
},[] )

return (
    <div>
    	  这是ref.current的值——ref.current:{ ref.current }
        <br></br>
        这是一个函数式组件————num:{  num }
    </div>
)

7.useContext

useContext是让子组件之间共享父组件传入的状态的。作用通俗地说是带着子组件去流浪。

  • 未使用useContext,我们有下列这样一个场景,我们父组件有传入一个值到不同的子组件中,示例给出的代码是2个这样的子组件,但是如果我需要添加的子组件特别多呢?总不能总是一个一个这样添加写入吧,而且如果传入的同一个变量名如果发生改变,还得一个个去改,所以我们可以用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>
    )
}

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

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

8.useReducer

以前是只能在类组件中使用Redux,现在我们可以通过useReducer在函数式组件中使用Redux。作用是可以从状态管理的工具中获取到想要的状态。

  • 如何使用useReducerRedux必须要有的内容就是仓库store和管理者reducer。而useReducer也是一样的,需要创建数据仓库store和管理者reducer,即示例代码注释处。然后我们就可以通过处的定义一个数组获取状态和改变状态的动作,触发动作的时候需要传入type类型判断要触发reducer哪个动作,然后进行数据的修改。需要注意的地方是,在reducerreturn的对象中,需要将state解构,否则状态就剩下一个num值了
  代码如下:
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>
    )
}

总结:

看完了吗?不知道你看完后是否有所收获,本文是从使用层面及一些日常应用示例上去分析的,并没有深层次剖析。如果你想要深层次了解掌握这几个Hooks,建议还是去看官方的源码哦。

  • 30
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值