hook的使用总结

hook简介

Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。践行函数式编程的方式,在函数组件中使用,获得class编写的特性。

useState和useReducer

useState

  • useState给一个state赋初始值,并且返回一个数组,一个用来读取state的值,一个用来设置state的值。
  • useState声明的state在发生变化的时候,会触发组件的更新。
  • setState更新state是异步更新策略,参数有两种,一种要更新的值,第二种是一个更新的函数,返回要更新的值。
  • setState不会进行合并更新,只会全量更新,所以参数或回调函数值应该要更新的值。
const [state, setState] = useState(10);
// 赋值修改
setState(20) // 此方法就是将state修改为20
// 函数修改
setState(current => {
    // current 是当前state的值
    return current + 20; // 修改state的值为40
   // return state + 20; // 这样也是可以的
})

useReducer

useState 的替代方案。它接收一个形如 (state, action) => newState 的 reducer,并返回当前的 state 以及与其配套的 dispatch 方法。(如果你熟悉 Redux 的话,就已经知道它如何工作了。)

用法简介

const [state, dispatch] = useReducer(reducer, initialState, init);
  • reducer是一个函数,该函数接受两个参数,一个是state一个是action, 是修改state值得函数。
  • initialState 初始值
  • init一个函数,用于初始化时调用的函数,返回的就是初始化的state
  • useReducer返回一个状态statedispatch,state是返回状态的值,dispatch是可以发布事件来更新state的函数,修改state值得韩式就是reducer。

指定初始值

function counter = () => {
    // 声明count,返回一个count 和 dispatch
    const [count, dispatch] = useReducer((state, action) => {
        // state 就是当前count的值
        // action 就是dispatch函数的参数,可以为任何类型的数据
        if (action === 'add') {
            return state + 1;
        } else {
            return state - 1;
        }
    }, 0) // 默认初始值为0

    return (
        <div>
            <h1>{count}</h1>
            {* 调用dispatch来更新count *}
            <button onClick={dispatch('sub')}> -1 </button>
            <button onClick={dispatch('sub')}> +1 </button>
        </div>
    )
}

惰性初始化

如果state初始化时需要有重置的操作或者state初始化的值十分复杂,都可以使用useReducer的第三个参数init

const initState = 10;
// 重置时会走该函数
const init = initState => {
    return initState;
}

function counter = () => {
    // 声明count,返回一个count 和 dispatch
    const [count, dispatch] = useReducer((state, action) => {
        // state 就是当前count的值
        // action 就是dispatch函数的参数,可以为任何类型的数据
        if (action === 'add') {
            return state + 1;
        } else if(action === 'sub') {
            return state - 1;
        } else if (action === 'reset') {
            return init(initState);
        }
    }, initState, init)

    return (
        <div>
            <h1>{count}</h1>
            {* 调用dispatch来更新count *}
            <button onClick={dispatch('sub')}> -1 </button>
            <button onClick={dispatch('sub')}> +1 </button>
            <button onClick={dispatch('reset')}> 重置 </button>
        </div>
    )
}

注意

  • useReducer返回的state和当前的一样时,是不会重新渲染该组件的,因为react使用Object.js来比较state。
  • 对于state有比较复杂的赋值操作时建议使用useReducer来声明和处理。

useEffect 和 useLayoutEffect

useEffect

  • useEffect可以执行副作用的操作
  • useEffect有两个参数,一个回调函数,一个数组类型变量(这里面的值必须是依赖项,就是使用useState等声明的变量,可以触发组件更新的变量)
  • useEffect会在每次render的时候都执行,可以通过参数控制只在初始化时执行、某些数据变化时执行
  • 相当于class组件的声明周期componentDidMount(组件渲染后执行)和componentDidUpdate(组件更新后执行)
  • useEffect是一个回调函数,如果返回一个函数时,则会在componentWillUnmount(组件销毁)时执行

只在初始化时执行

在第二个参数中传递[]空数组,则只在初始化时调用

useEffect(() => {
    // 初始化时获取list数据
    getList();
}, [])

在初始化和render更新时执行

第二个参数不穿时,会在初始化和更新时执行

useEffect(() => {
    getList();
})

在某些数据变化时执行

// 在count数据变化的时候执行
useEffect(() => {
    getList();
}, [count]) // 数组中可以放许多的变量,在这些数据变化时都会执行

useLayoutEffect

  • 当需要处理dom时,使用useEffect会导致闪屏问题
  • useLayoutEffect会在DOM更新完成后立即执行,在浏览器进行回执之前运行
  • 回阻塞浏览器的绘制

useContext

介绍

  • 接受一个context(React.createContext的返回值)对象
  • useContext帮助我们跨越组件层级直接传递变量,实现共享,解决了组件之间传值的问题。
  • context对象提供provider属性将数据可以通过垮组件访问,通过value将值传给子组件
  • useContext获取的数据是跟随数据源里的数据的变化而变化的。
const value = useContext(MyContext);

使用

// 父组件 parent.js
import React, { useState, createContext } from 'react'
import Children from './children.js'

const Parent = () => {
    const [count, setCount] = useState(10);
    const CountContext = createContext();
    
    return (
        <>
            <CountContext.Provider value={{count}}>
                <Children countContext={CountContext}/>
            </CountContext.Provider>
            <h1>{ count }</h1>
            <button onClick={() => setCount(count + 1)}>
                加1
            </button>
        </>
    )
}

// 子组件 children.js
import React, { useContext } from 'react'

const Children = props => {
    // 这里的countContext是从组件传递过来的
    // 也可以把createCount单独放到模块里面进行引用
    // 引用模式应该比props好,可以适应组件嵌套的问题
    const { countContext } = props;
    // 这里的数据是随着父组件中的count数据变化而变化的
    const countData = useContext(countContext);
    
    return (
        <h1>{ countData.count }</h1>
    )
}

useRef 与 useImperativeHandle

useRef

const refContainer = useRef(initialValue);
  • useRef返回一个可变的ref对象,其.current属性就是被初始化传入的参数。
  • useRef中的值发生变化不会触发组件的更新
  • useRef可以用来保存任何可变值。
  • 绑定到原生html上面可以获取该标签的方法,绑定到自定义组件上,则不要通过useImperativeHandle将部分方法和属性暴露出来。
  • useImperativeHandle可以在使用ref的时候自定义暴露给父组件的实例值和方法,需要和forwardRef一起使用

useImperativeHandle

useImperativeHandle(ref, createHandle, [deps])
  • ref:定义 current 对象的 ref createHandle:一个函数,返回值是一个对象,即这个 ref 的 current对象
  • [deps]:即依赖列表,当监听的依赖发生变,useImperativeHandle 才会重新将子组件的实例属性输出到父组件
  • ref 的 current 属性上,如果为空数组,则不会重新输出。

实例

// 父组件 parent.js
import React, {useRef} from 'react'
import Children from './children'

const Parent = () => {
    const childRef = useRef();
    
    return (
        <>
            <Children ref={childRef} />
            <button onClick={() => childRef.current.addCount()} >+1</button>
        </>
    )
}
// children.js
import React, {useState, useImperativeHandle, forwardRef} from 'react'

// 第二个参数才是ref,第一个是props
const Children = (props, ref) => {
    const [count, setCount] = useState(1)
    
    useImperativeHandle(ref, () => {
        addCount: setCount
    })
    
    return (
        <h1>{count}</h1>
    )
}

// 需要使用forwardRef进行一次转发
export default forwardRef(Children);

useCallback和useMemo

  • 都是用于数据缓存的方法
  • 都可以根据依赖项进行刷新
  • 主要是用在不需要随着组件更新而更新的情况时,用于优化部分复杂函数更新问题
  • useCallback返回函数,并不调用他们
  • useMemo 调用函数,返回执行的结果

用法

// useCallback 返回一个函数
const memoizedCallback = useCallback(
  () => {
    doSomething(a, b);
  },
  [a, b],
);
// useMemo  执行函数,返回执行的结果
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

例子

// parent.js
import React, {useState, useCallback, useMemo} from 'react'
import ChildrenComponent from './Children.js'

const Parent = () => {
    const [count, setCount] = useState(10)
    const [num, setNum] = useState(10)
    
    // 只有count变化的时候countName才会更新,可以作为优化部分
    const countName = useMemo(() => `年龄:${count}`, [count]);
    
    // 这样,只有在num更新的时候,children组件才会更新
    const setDataNum = useCallback(() => setNum(num + 10), [num]);
    
    return (
        <div>
            <h1>
                {countName}
            </h1>
            <button onClick={() => setCount(count + 1)}>+1</button>
            <H1>NUM {num}</H1>
            <ChildrenComponent setData={setDataNum}/>
        </div>
    )
}

// children.js
import React from 'react';

const Children = ({setData}) => {
    return (
        <button onClick={() => setData()}>加10</button>
    )
}

export React.memo(Children)

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • github的MD[https://github.com/liusanhong/study/blob/master...
    半个木头人阅读 2,256评论 0 0
  • 主要介绍 useState useEffect useReducer useContext 用法 你还在为...
    叫我苏轼好吗阅读 27,567评论 3 41
  • 什么是React Hook? 一个特殊的函数,用于钩入React的特性。它鼓励我们将通用逻辑封装成Hook而不是工...
    南山码僧阅读 3,764评论 0 7
  • Hook 是 react 16.8 推出的新特性,具有如下优点:Hook 使你在无需修改组件结构的情况下复用状态逻...
    林木木road阅读 4,198评论 0 1
  • 一、组件类 React的核心是组件, 在v16.8之前,组件的标准写法是类(class)。 以下为一个简单的组件类...
    郭_小青阅读 4,055评论 1 5

友情链接更多精彩内容