React Hooks钩子

一、useState()

        useState 可以使函数组件像类组件一样拥有 state,函数组件通过 useState 可以让组件重新渲染,更新视图。

        语法、参数及返回值说明:

const [ ①state , ②dispatch ] = useState(③initData)

        ① state,目的提供给 UI ,作为渲染视图的数据源。

        ② dispatchAction 改变 state 的函数,可以理解为推动函数组件渲染的渲染函数。

        ③ initData 有两种情况,第一种情况是非函数,将作为 state 初始化的值。 第二种情况是函数,函数的返回值作为 useState 初始化的值。

import React, { useState } from "react"
const DemoState = (props) => {
  /* number为此时state读取值 ,setNumber为派发更新的函数 */
  let [number, setNumber] = useState(0) /* 0为初始值 */
  return (
    <div>
      <p>{number}</p>
      <button onClick={() => {
        setNumber(number + 1)
        console.log(number) /* 这里的number是不能够即使改变的  */
      }}>点我</button>
    </div>
  )
}
export default DemoState

        useState 注意事项:

                ① 在函数组件一次执行上下文中,state 的值是固定不变的。

function Index() {
    const [number, setNumber] = React.useState(0)
    const handleClick = () => setInterval(() => {
        // 此时 number 一直都是 0
        setNumber(number + 1)
    }, 1000)
    return <button onClick={handleClick}>点击{number}</button>
}

                ② 如果两次 dispatchAction 传入相同的 state 值,那么组件就不会更新。

export default function Index() {
    const [state, dispatchState] = useState({ name: 'alien' })
    const handleClick = () => { // 点击按钮,视图没有更新。
        state.name = 'Alien'
        dispatchState(state) // 直接改变 `state`,在内存中指向的地址相同。
    }
    return <div>
        <span> {state.name}</span>
        <button onClick={handleClick}>changeName++</button>
    </div>
}

                ③ 当触发 dispatchAction 在当前执行上下文中获取不到最新的 state, 只有再下一次组件 rerender 中才能获取到。

二、useContext()

        useContext():共享状态钩子。作用就是可以做状态的分发,在React16.X以后支持,避免了react逐层通过Props传递数据。

        Context:一种组件间通信方式, 常用于【祖组件】与【后代组件】间通信

使用语法和说明:

1、创建Context容器对象:

const XxxContext = React.createContext()  

2、渲染子组件时,外面包裹xxxContext.Provider, 通过value属性给后代组件传递数据:

<xxxContext.Provider value={数据}>
    <子组件 />
</xxxContext.Provider>

3、后代组件读取数据:

const {} = useContext(XxxContext)

例如:A组件和B组件需要共享一个状态:

import React, { useContext } from "react";
const HookTest = ()=> {
    const AppContext = React.createContext();
    const A = ()=> {
        const { name } = useContext(AppContext)
        return (
            <p>
                我是A组件,我的名字是:{ name };
                <span>我是A的子标签:{ name }</span>
            </p>
        )
    }
    const B= ()=> {
        const { name } = useContext(AppContext);
        return (
            <p>我是B组件,名字是: { name }</p>
        )
    }
    return (
        <AppContext.Provider value={{ name: '张三'}}>
            <A />
            <B />
        </AppContext.Provider>
    )
}
export default HookTest;
三、useEffect()

        React hooks也提供了 api ,用于弥补函数组件没有生命周期的缺陷。其本质主要是运用了 hooks 里面的 useEffect , useLayoutEffect,还有 useInsertionEffect。其中最常用的就是 useEffect 。我们首先来看一下 useEffect 的使用。

        useEffect 基础介绍:

useEffect(() => {
    return destory
}, dep)

        useEffect 第一个参数 callback, 返回的 destory , destory 作为下一次callback执行之前调用,用于清除上一次 callback 产生的副作用。

        第二个参数作为依赖项,是一个数组,可以有多个依赖项,依赖项改变,执行上一次callback 返回的 destory ,和执行新的 effect 第一个参数 callback 。

        对于 useEffect 执行, React 处理逻辑是采用异步调用 ,对于每一个 effect 的 callback, React 会向 setTimeout回调函数一样,放入任务队列,等到主线程任务完成,DOM 更新,js 执行完成,视图绘制完毕,才执行。所以 effect 回调函数不会阻塞浏览器绘制视图。

        useEffect 基础用法:

import React, { useState, useEffect, useRef, } from "react"

/* 模拟数据交互 */
function getUserInfo(a) {
  return new Promise((resolve) => {
      setTimeout(() => {
          resolve({
              name: a,
              age: 16,
          })
      }, 500)
  })
}

const Demo = ({ a }) => {
  const [userMessage, setUserMessage] = useState({})
  const div = useRef()
  const [number, setNumber] = useState(0)
  /* 模拟事件监听处理函数 */
  const handleResize = () => { }
  /* useEffect使用 ,这里如果不加限制 ,会是函数重复执行,陷入死循环*/
  useEffect(() => {
      /* 请求数据 */
      getUserInfo(a).then(res => {
          setUserMessage(res)
      })
      /* 定时器 延时器等 */
      const timer = setInterval(() => console.log(666), 1000)
      /* 操作dom  */
      console.log(div.current) /* div */
      /* 事件监听等 */
      window.addEventListener('resize', handleResize)
      /* 此函数用于清除副作用 */
      return function () {
          clearInterval(timer)
          window.removeEventListener('resize', handleResize)
      }
      /* 只有当props->a和state->number改变的时候 ,
        useEffect副作用函数重新执行 ,如果此时数组为空[],
        证明函数只有在初始化的时候执行一次相当于componentDidMount 
      */
  }, [a, number])
  return (<div ref={div} >
      <span>{userMessage.name}</span>
      <span>{userMessage.age}</span>
      <div onClick={() => setNumber(1)} >{number}</div>
  </div>)
}


export default Demo

如上在 useEffect 中做的功能如下:

  • ① 请求数据。
  • ② 设置定时器,延时器等。
  • ③ 操作 dom , 在 React Native 中可以通过 ref 获取元素位置信息等内容。
  • ④ 注册事件监听器, 事件绑定,在 React Native 中可以注册 NativeEventEmitter 。
  • ⑤ 还可以清除定时器,延时器,解绑事件监听器等。
四、useReducer()

        useReducer():Action钩子。在使用React的过程中,如遇到状态管理,一般会用到Redux。而React本身是不提供状态管理的。而useReducer() 提供了状态管理。

        首先,关于redux我们都知道,其原理是通过用户在页面中发起action,从而通过reducer方法来改变state,从而实现页面和状态的通信。

        而Reducer的形式是(state, action) => newstate。hooks的形式如下:

语法格式:

const [state, dispatch] = useReducer(reducer, initialState)

参数、返回值说明:

        它接受 reducer函数 和 状态的初始值 作为参数,返回一个数组,其中第一项为当前的状态值,第二项为发送action的dispatch函数

例如:使用useReducer()实现一个计数器

import { useReducer } from "react";
const HookReducer = () => {
    const reducer = (state, action) => {
        if (action.type === 'add') {
            return {
                ...state,
                count: state.count + 1
            }
        } else {
            return state
        }
    }
    const addCount = () => {
        dispatch({
            type: 'add'
        })
    }
    const [state, dispatch] = useReducer(reducer, { count: 0 })
    return (
        <>
            <p>{state.count}</p>
            <button onClick={addCount}>useReducer</button>
        </>
    )
}
export default HookReducer;

        通过代码可以看到,使用useReducer()代替了Redux的功能,但useReducer无法提供中间件等功能,假如有这些需求,还是需要用到redux。

五、useRef()

        userRef():可以在函数组件中存储、查找组件内的标签或任意其它数据

语法和参数说明:

const refContainer = useRef()

        useRef返回一个可变的ref对象,useRef接受一个参数绑定在返回的ref对象的current属性上,返回的ref对象在整个生命周期中保持不变。

        作用:保存标签对象,功能与React.createRef()一样

例子:input上绑定一个ref,使得input在渲染后自动焦点聚焦

import { useRef, useEffect } from "react";
const RefComponent = () => {
    let inputRef = useRef(null);
    useEffect(() => {
        inputRef.current.focus();
    })
    return (
        <input type="text" ref={inputRef} />
    )
}
六、useMemo()

        useMemo(): 主要用来解决使用React hooks产生的无用渲染的性能问题

语法和参数说明:

const cacheSomething = useMemo(create,deps)

        create:第一个参数为一个函数,函数的返回值作为缓存值
        deps: 第二个参数为一个数组,存放当前 useMemo 的依赖项,在函数组件下一次执行的时候,会对比 deps 依赖项里面的状态,是否有改变,如果有改变重新执行 create ,得到新的缓存值。
        cacheSomething:返回值,执行 create 的返回值。如果 deps 中有依赖项改变,返回的重新执行 create 产生的值,否则取上一次缓存
        使用function的形式来声明组件,失去了shouldCompnentUpdate(在组件更新之前)这个生命周期,也就是说没有办法通过组件更新前条件来决定组件是否更新。

        而且在函数组件中,也不再区分mount和update两个状态,这意味着函数组件的每一次调用都会执行内部的所有逻辑,就带来了非常大的性能损耗。

useMemo原理:

        useMemo 会记录上一次执行 create 的返回值,并把它绑定在函数组件对应的 fiber 对象上,只要组件不销毁,缓存值就一直存在,但是 deps 中如果有一项改变,就会重新执行 create ,返回值作为新的值记录到 fiber 对象上。

useMemo应用场景:

        可以缓存 element 对象,从而达到按条件渲染组件,优化性能的作用。
如果组件中不期望每次 render 都重新计算一些值,可以利用 useMemo 把它缓存起来。
可以把函数和属性缓存起来,作为 PureComponent 的绑定方法,或者配合其他Hooks一起使用

七、useCallback()

        useCallback(): 主要是为了性能的优化

        useCallback(fn, deps) 相当于 useMemo(() => fn, deps)

        可以认为是对依赖项的监听,接受一个回调函数和依赖项数组。

                1、useCallback会返回一个函数的memoized(记忆的)值。
                2、该回调函数仅在某个依赖项改变时才会
                3、在依赖不变的情况下,多次定义的时候,返回的值是相同的

import { useState, useCallback } from "react";

const CallbackComponent = () => {
    let [count, setCount] = useState(1);
    let [num, setNum] = useState(1);

    const memoized = useCallback(() => {
        return num;
    }, [count])
    console.log("记忆:", memoized());
    console.log("原始:", num);
    return (
        <>
            <button onClick={() => { setCount(count + 1) }}> count+</button>
            <button onClick={() => { setNum(num + 1) }}> num+</button>
        </>
    )
}
export default CallbackComponent

如果没有传入依赖项数组,那么记忆函数在每次渲染的时候都会更新。

八、useLayoutEffect()

        useLayoutEffect() :和useEffect相同,都是用来执行副作用,但是它会在所有的DOM变更之后同步调用effect。useLayoutEffect和useEffect最大的区别就是一个是同步,一个是异步。

        从这个Hook的名字上也可以看出,它主要用来读取DOM布局并触发同步渲染,在浏览器执行绘制之前,useLayoutEffect 内部的更新计划将被同步刷新。

        官网建议还是尽可能的是使用标准的useEffec以避免阻塞视觉更新。

九、useImperativeHandle()

        useImperativeHandle(): 可以在使用 ref 时自定义暴露给父组件的实例值。

        就是说:当使用父组件把ref传递给子组件的时候,这个Hook允许在子组件中把自定义实例附加到父组件传过来的ref上,有利于父组件控制子组件。

使用方式:

import { useEffect, useRef, useImperativeHandle } from "react";
import { forwardRef } from "react";

function FancyInput(props, ref) {
    const inputRef = useRef();
    useImperativeHandle(ref, () => ({
        focus: () => {
            inputRef.current.value = "Hello";
        }
    }));
    return <input ref={inputRef} />;
}
FancyInput = forwardRef(FancyInput);

const ImperativeHandleTest = () => {
    let ref = useRef(null);
    useEffect(() => {
        console.log(ref);
        ref.current.focus();
    })
    return (
        <>
            <FancyInput ref={ref} />
        </>
    )
}
export default ImperativeHandleTest

自定义Hooks
        有时候我们需要创建自己想要的Hooks,来满足更便捷的开发,就是根据业务场景对其它Hooks进行组装,从而得到满足自己需求的钩子。

        自定义 Hooks:是一个函数,其名称以 “use” 开头,函数内部可以调用其他的 Hook

        自定义Hooks:可以封装状态,能够更好的实现状态共享

        自定义hooks可以说成是一种约定而不是功能。当一个函数以use开头并且在函数内部调用其他hooks,那么这个函数就可以成为自定义hooks

例如:

import { useState, useEffect } from "react";
const usePerson = ({ name }) => {
    const [loading, setLoading] = useState(true)
    const [person, setPerson] = useState({})

    useEffect(() => {
        setLoading(true)
        setTimeout(() => {
            setLoading(false)
            setPerson({ name })
        }, 2000)
    }, [name])
    return [loading, person]
}
const AsyncPage = (name) => {
    const [loading, person] = usePerson(name)
    return (
        <>
            {loading ? <p>Loading...</p> : <p>{person.name}</p>}
        </>
    )
}

const PersonPage = () => {
    const [state, setState] = useState('')
    const changeName = (name) => {
        setState(name)
    }
    return (
        <>
            <AsyncPage name={state} />
            <button onClick={() => { changeName('郭靖') }}>郭靖</button>
            <button onClick={() => { changeName('黄蓉') }}>黄蓉</button>
        </>
    )
}
export default PersonPage;

        上面代码中,封装成了自己的Hooks,便于共享。其中,usePerson()为自定义Hooks它接受一个字符串,返回一个数组,数组中包括两个数据的状态,之后在使用usePerson()时,会根据传入的参数不同而返回不同的状态,然后很简便的应用于我们的页面中。

这是一种非常简单的自定义Hook。如果项目大的话使用自定义Hook会抽离可以抽离公共代码,极大的减少我们的代码量,提高开发效率。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值