总结React Hook API 的功能与作用

227 阅读4分钟

前言

React Hook是React-16.8开始出现的新特性,它可以让你在不编写class的情况下使用state及其它的React特性,Hook是一个特殊的函数表现。

React Hook 解决了什么问题?

  • 避免疯狂式嵌套,可读性提高。
  • 函数式组件,比class语法更加已读和理解。
  • class组件钩子函数太多太复杂。
  • 解决HOC和render props的缺点。
  • UI和逻辑更方便解耦。
  • 解决组件之间的副作用。

useState

在函数组件中,可以用来管理组件的状态。

import React, { useEffect, useState } from "react";
function ComponentApp(){
    const [count,setCount] = useState(0);
    
    return (
        <>
            <div onClick={()=>setCount(count+1)}>+</div>
            {count}
            <div onClick={()=>setCount((pv)=>pv-1)}>-</div>
        </>
    )
};

说明:

  • 只能在函数最顶层使用hook,不要再循环、条件判断或子函数中调用。
  • 只能在react的函数组件中调用hook,不能是普通函数,也不能是class组件。

useEffect

接收一个包含命令式,且可能有副作用代码的函数。

import React, { useEffect, useState } from "react";
function ComponentApp(){
    const [count,setCount] = useState(0);
    useEffect(()=>{
        // 这里执行相当于react class中DidMount和DidUpdate钩子。
        console.log("count状态更新了,值:",count);
        return ()=>{
            // unMount
            console.log("卸载状态");
        }
    },[count]);
    
    return (
        <>
            {count}
            <div onClick={()=> setCount(count+1)}>+</div>
        </>
        
    )
};

功能说明:

  • 支持在组件内完成挂载、状态更细和卸载的能力。
  • 第一个参数是一个callback,可以用来做一些副作用,比如异步请求,修改外部参数等行为。
  • useEffect的第二个参数source是一个触发条件,类型为数组,如果数组中观测的值发生变化才会触发useEffect的callback回调。
    • 没有传入参数时,默认每次render更改时都会重新执行回调。
    • 传递一个[],只有在组件初始化或销毁的时候才会触发;
    • 传入[source],在依赖state发生改变时,才重新执行该回调。
  • 在组件里useEffect可以编写多个,会按照顺序进行执行。

useLayoutEffect

与useEffect函数类似,但会在DOM修改后同步触发执行,并且比useEffect先执行,会阻塞浏览器的函数渲染。

useEffect执行顺序:组件更新挂载完成,DOM绘制完成,执行useEffect回调。 useLayoutEffect执行顺序:组件更新挂载完成,执行useLayoutEffect回调,DOM绘制完成。

import React, { useEffect,useLayoutEffect, useState } from "react";
function ComponentApp(){
    const [count,setCount] = useState(0);
    useEffect(()=>{
        // 模拟耗时操作 
        const passedTime = (new Date()).getTime(); 
        const now = (new Date()).getTime(); 
        while(now - oassedTime < 1000){}; 
        if(!count){
            setCount(1+Math.random()*1000)
        }
    },[count]);
    
    useLayoutEffect(()=>{
        // count状态更新了, DOM再进行更新。
        
        const passedTime = (new Date()).getTime(); 
        const now = (new Date()).getTime(); 
        while(now - oassedTime < 1000){}; 
        if(!count){
            setCount(1+Math.random()*1000)
        }
        return ()=>{
            // unMount
            console.log("卸载状态");
        }
    },[count]);
    
    return (
        <>
            {count}
            <div onClick={()=> setCount(count+1)}>+</div>
        </>
        
    )
};

useMemo

提升性能,给子组件传递状态时绑定这个函数,可以避免组件更新时,子组件进行不必要的更新。

useCallback

通useMemo类似,给子组件传递函数时绑定这个函数,可以避免组件更新时,子组件进行不必要的更新。

useContext

组件之间的通信,组件深入传值时,props不如useContext方便。

有两种方式: 首先通过export const MyContext = React.createContext({data:{}}),暴露要传递的状态。

  1. 配合<MyContext.Provider data={{val:"test"}}>进行传递,在子组件中使用。
  2. 在子组件中import {MyContext} from "./MyContext",在方法里调用 const obj = useContext(MyContext);

如果不想使用.Provider作为提供者传递数据,则可以在子组件中import之后,直接使用useContext进行操作。

// MyContext.js
import React from "react";

const MyContext = React.createContext({
    data: {
        val: "test"
    }
});

export MyContext;

// hook component app
import { MyContext } from "./MyContext";
import React, { useEffect, useContext, useState } from "react";
function ComponentApp(props={}){
    const MyContext = useContext(MyContext);
    useEffect(()=>{
    
    },[]);
    return (
        <div>{MyContext.data.val}</div>
    );
};
  • MyContext.js 每次返回的都是一个新的对象。

useReducer

用于集中管理状态,可做全局或局部的状态管理。

全局状态管理要配合 <MyContext.Provider>、React.createContext、useContext 进行。

// stores.js
export default {
    state: {
        name: "小明",
        age: 16,
        gender: "男"
    }
}
// reducer.js
export function reduceCount(state,action){
    switch(action.type){
        case "add":
            return {...state, count: state.count+1}
            break;
        case "sub":
            return {...state, count: state.count-1}
            break;
        default:
            return state;
    }
};
// myContext.js
export function MyContext(){
    return React.createContext({
        data: {
            val: "test"
        }
    });
};

// App.jsx
import React from "react";

function App(){
    
    return (
        <>
            <ChildComponent />
        </>
    )
};
export default App;

// ChildComponent.jsx
import React, { useEffect, useContext, useReducer, useState } from "react";
function ChildComponent(props={}){
    const [state,dispatch] = useReducer();
    
    return (
    
    )
};

useRef

用于在函数组件中获取DOM元素。

import React, { useEffect, useRef } from "react";
function ComponentApp(){
    const inputRef = useRef(null);
    useEffect(()=>{
        let val = inputRef.current.value;
        console.log(val);
        
        return ()=>{};
    },[]);
    
    return (
        <input type="text" ref={inputRef} />
    )
};

功能:

  • 返回一个可变的 ref 对象,该对象只有个 current 属性,初始值为传入的参数( initialValue )。
  • 返回的 ref 对象在组件的整个生命周期内保持不变
  • 当更新 current 值时并不会 re-render ,这是与 useState 不同的地方
  • 更新 useRef 是 side effect (副作用),所以一般写在 useEffect 或 event handler 里
  • useRef 类似于类组件的 this

useImperativeHandle

功能:

  • 可以让父组件操作子组件的方法

forwardRef

功能:

  • 可以在组件引用时写ref,将ref透传到子组件内。

Hook的优点

  • 函数式组件Hooks只能在函数组件中使用,直接就强制避免了类结构对开发者、代码分析器等角色造成的困惑性。
  • 组件层级变浅,提高可维护性,有利于编写复杂组件,有利于改善嵌套地狱问题。
  • 极简的代码风格,可读性高,将特定的代码整合,方便后期阅读。

参考文章

Hooks API Reference Hooks FAQ

原文链接

总结React Hook API 的功能与作用

您觉得还不错的话,感谢点赞+关注~