React Hooks 使用和总结

1,831 阅读5分钟

React Hooks是 React v16.8 新增特性。本文假设您对 Hooks 有一个基本的理解, 你应该读过 Hook 简介 ,并且运行过里面例子中的代码。以下我们将列举和总结一些 React Hooks 使用的注意点。

一、Hook 规则

Hook 规则 中提到:

只在最顶层使用 Hook,不要在循环,条件或嵌套函数中调用 Hook
React 靠的是 Hook 调用的顺序。因为我们的示例中,Hook 的调用顺序在每次渲染中都是相同的,所以它能够正常工作。

下面是把 useState 放在 if 条件语句中,运行直接被拒绝,即使没有不被允许,我们也不该这么写。

import React, { useState } from 'react';
function Count (){
    const [count,setCount] = useState(0);
    if (true){
        const [count1,setCount1] = useState(0);
    }
    return (
        <div>
            <p>you click {count} times</p>
            <button onClick={()=>{setCount(count+1)}}>点击</button>
        </div>
    )
}
export default Count;

运行结果如下图

二、Effect Hook 的使用

Effect Hook 用于替换生命周期挂钩,如 componentDidMount、 componentDidUpdate 和 componentWillUnmount。

useEffect 接收两个参数,第一个是匿名函数,在 React 组件渲染后执行。第二个参数为可选,接收的是依赖组成的数组,要有数组内的某一元素更改,才执行该 effect 。

useEffect 第一个参数中的匿名函数可以有返回值,返回值是一个函数,这个函数会在下一个 effect 执行之前调用。

import React, { useState,useEffect } from 'react';

function Effect (){
    const [count,setCount] = useState(0);

    useEffect(()=>{
        console.log(`useEffect count =  ${count} `);
        return ()=>{ console.log(`count effect return pre count ${count}`)}
    },[count]);

    return (
        <div>
            <p>
                you click {count} timesss
            </p>
            <button onClick={()=>{setCount(count+1)}}>点击</button>
        </div>
    )
}

export default Effect;

执行以上代码:

1、useEffect 不传第二个参数时,匿名函数会在组件每次渲染后都执行一遍。

useEffect(()=>{
    console.log(`render`);
});

2、useEffect 的第二个参数传入的是空数组时,该 effect 只在组件第一次加载后执行,这个与 Component 中的 componentDidMount 对应。返回值则会在组件卸载前执行,与 componentWillUnmount 对应。

useEffect(()=>{
    console.log(`componentDidMount`);
    return ()=>{ console.log(`componentWillUnmount`); }
},[]);

3、useEffect 第一个参数中的匿名函数的返回值一般被用来做一些清除工作。

就如我们在 componentDidMount 中设置订阅,并在 componentWillUnmount 中清除它。我们可以查看 官网中使用 Hook 的示例。那假如我们要在组件卸载时清除订阅,可以在依赖项( useEffect 的第二个参数)加上一个空数组。
我们借助 react-router 来看看组件加载、更新和卸载时 useEffect 的工作。看代码

import React, { useState,useEffect } from 'react';
import {BrowserRouter as Router,Route,Link} from "react-router-dom"

function Count (){
    const [count,setCount] = useState(0);
    useEffect(()=>{
        console.log(`Count useEffect current ${count}`);
        return ()=>{console.log(`count useEffect return pre ${count}`);}
    },[count]);

    useEffect(()=>{
        console.log(`Count []`);
        return ()=>{console.log(`Count [] return`);}
    },[]);

    return (
        <div>
            <p>you click {count} times</p>
            <button onClick={()=>{setCount(count+1)}}>点击</button>
        </div>
    )
}

function Component2 (){
    useEffect(()=>{
        console.log("Component2 []");
        return ()=>{console.log("Component2 [] return");}
    },[])
    return (<h2>Component2</h2>)
}
function Effect (){
    
    return (
        <div>
            <Router>
                <ul>
                    <li><Link to="/count">计数器</Link></li>
                    <li><Link to="/component2">组件2</Link></li>
                </ul>
                <Route path="/count" exact component={Count} />
                <Route path="/component2" component={Component2} />
            </Router>
        </div>
    )
}

export default Effect;

运行,我们可以看到如下的操作和打印:


1、点击 计数器 ,加载 Count 组件,两个 effect 都执行并打印。
2、点击按钮,先执行上一个 effect 返回的函数,所以先打印 ”count useEffect return pre 0“ 。接着执行 此次 effect 函数,打印 “Count useEffect current 1”。以此类推。
3、点击 组件2 ,打印了 “count useEffect return pre 3” 和 “Count [] return”。说明 Count 组件在卸载前,所有的 effect 都执行了 return 函数。接着打印 “Component2 []”, 组件 Component2 加载完成。
4、点击 计数器 , 打印 “Component2 [] return” 、“Count useEffect current 0” 、“Count []”,这个过程的分析与上面一致,不在赘述。

至此,Effect Hook 先告一段落。

三、useContext 共享状态勾子

在此之前,希望您有看过,React官网 useContext 简介React官网 useReducer 简介 中的内容,因为以下例子是两者的结合。
接下来我们使用 createContext 创建一个ColorContext,然后使用 Context.Provider 共享数据。使用 useReducer 分发状态。 我们看到父组件 Color.js 的代码,value 值是color和dispatch,真正要共享的值是color,dispatch也被共享是因为子组件要用它来改变color的值。

import React, { createContext ,useReducer} from 'react';
const ColorContext = createContext({});
const UPDATE_COLOR = "UPDATE_COLOR";
const reducer = (state,action)=>{
    switch (action.type){
        case UPDATE_COLOR:
            return action.color;
        default :
            return state;
    }
}

const Color = (props)=>{
    const [color,dispatch] = useReducer(reducer,"blue");
    return (
        <ColorContext.Provider value = {{color,dispatch}}>
            {props.children}
        </ColorContext.Provider>
    )
}

export {
    ColorContext,
    Color,
    UPDATE_COLOR
}

创建要共享父组件状态的子组件,通过 useContext 和之前创建的 ColorContext 获取共享值Color。

import React,{useContext} from 'react';
import {ColorContext} from "./Color"

function ShowArea (){

    const {color} = useContext(ColorContext);
    return (
        <div style={{color:color}}>
            现在是{color}色
        </div>
    )

}
export default ShowArea;

创建能改变共享状态的子组件,通过 useContext 和之前创建的 ColorContext 获取 useReducer 函数返回的 dispatch(这个 dispatch 也是共享值)。

import React, { useContext } from 'react';
import {ColorContext,UPDATE_COLOR} from "./Color";

function Buttons (){

    const {dispatch} = useContext(ColorContext);
    return (
        <div>
            <button onClick={()=>{dispatch({type:UPDATE_COLOR,color:"red"})}}>红色</button>
            <button onClick={()=>{dispatch({type:UPDATE_COLOR,color:"yellow"})}}>黄色</button>
        </div>
    )
}

export default Buttons;

最后的使用是这样的:

import React, { Component } from 'react';
import ShowArea from './ShowArea';
import Buttons from './Buttons';
import {Color} from "./Color";

function ContextReducer (){
    return (
        <div>
            <Color>
                <ShowArea/>
                <Buttons/>
            </Color>
        </div>
    )
}

export default ContextReducer;

我们来看看运行情况:

如果看完觉得对您有帮助,还有劳点个赞,谢谢啦。

参考文章 (感谢)

React官网 Hook 简介
React Hooks 视频教程_技术胖
React Hooks — How To Use useState and useEffect Example