当前位置: 首页 > 工具软件 > react-things > 使用案例 >

【React】- React Hooks

胡翔
2023-12-01

一、概念

为什么会有Hooks?

介绍Hooks之前,首先要说一下React的组件创建方式,一种是类组件,一种是纯函数组件,并且React团队希望,组件不要变成复杂的容器,最好只是数据流的管道。开发者根据需要,组合管道即可。也就是说组件的最佳写法应该是函数,而不是类。
但是我们知道,在以往开发中类组件和函数组件的区别是很大的,纯函数组件有着类组件不具备的多种特点:

  • 纯函数组件没有状态
  • 纯函数组件没有生命周期
  • 纯函数组件没有 this
    这就说明了,函数组件只能做UI展示的功能,涉及到状态的管理与切换,我们不得不使用类组件或者redux,但我们知道类组件也是优缺点的,比如:遇到简单的页面,代码会显得很重,并且每创建一个类组件,都要去继承一个React实例;至于Redux会更复杂。

例如:使用类组件做一个简单的计数器

import React,{PureComponent} from "react";
class Addcount extends PureComponent {
    constructor(props) { //构造函数在有状态时出现
        super(props);
        this.state = {
            count : 0
        }
    }
    addcount = () => {
        let newCount = this.state.count;
        this.setState({
            count : newCount += 1
        })
    }
    render() {
        let { count } = this.state
        return (
            <>
                <p>{ count }</p>
                <button onClick= { this.addcount }>count++</button>
            </>
        )
    }
}
export default Addcount;

可以看出来,上面的代码确实很重。
为了解决这种类组件功能齐全却很重,纯函数很轻便却有上文几点重大限制,React队设计了React Hooks
React Hooks就是加强版的函数组件,我们可以完全不使用class,就能写出一个全功能的组件

二、Hooks的含义

‘Hooks’的单词意思为’钩子’。
React Hooks 的意思是,组件尽量写成纯函数,如果需要外部功能和副作用,就用钩子把外部代码’钩’进来。 而React Hooks 就是我们所说的"钩子"。
Hooks怎么用,“你需要写什么功能,就用什么钩子”。对于常见的功能,React为我们提供了一些常用的钩子,当然有特殊需要时,我们也可以写自己的钩子。下面时React Hooks 为我们提供的默认的四种最常用的钩子。

  • useState()
  • useContext()
  • useReducer()
  • useEffect()

不同的钩子为函数引入不同的外部功能,上面四种钩子都带有use前缀,React Hooks约定,钩子一律使用use前缀命名。所以,自己定义的钩子都要命名为useXXX

三、React Hooks的用法

1、useState():状态钩子。

纯函数没有状态,useState() 用于为函数组件引入状态。

使用Hooks重写计数器

import React,{ useState } from "react";

const NewCount = ()=> {
    const [ count,setCount ] = useState(0)
    const addCount= ()=> {
        let newCount = count;
        setCount(newCount +=1)
    }
   return (
       <>
           <p> { count }</p>
           <button onClick={ addCount }>Count++</button>
       </>
   )
}
export default NewCount;

用函数组件实现了一个功能完全一样的计数器,代码看起来更加的轻便简洁,没有了继承,没有了渲染逻辑,没有了生命周期等。这就是hooks存在的意义。在useState()中,它接受状态的初始值作为参数,即上例中计数的初始值,它返回一个数组,其中数组第一项为一个变量,指向状态的当前值,类似this.state;第二项是一个函数,用来更新状态,类似setState。该函数的命名,约定为set前缀加状态的变量名。

2、useContext():共享状态钩子。

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

例如:现在假设有A组件和B组件需要共享一个状态。

import React, { useContext } from "react";
const HookTest = ()=> {
    const AppContext = React.createContext({}); //创建全局的对象AppContext
    const A = ()=> {
        const { name } = useContext(AppContext); //解构赋值:获取全局对象AppContext中name
        return (
            <p>
                我是A组件,我的名字是:{ name };
                <span>我是A的子标签:{ name }</span>
            </p>
        )
    }
    const B= ()=> {
        const { name } = useContext(AppContext); //解构赋值:获取全局对象AppContext中name
        return (
            <p>我是B组件,名字是: { name }</p>
        )
    }
    return (
        <AppContext.Provider value={{ name: '周杰伦'}}>
            <A />
            <B />
        </AppContext.Provider>
    )
}
export default HookTest;

3、useReducer():Action钩子。

在使用React的过程中,如遇到状态管理,一般会用到Redux,而React本身是不提供状态管理的。而useReducer()提供了状态管理。
首先,关于redux我们都知道,其原理是通过用户在页面中发起action,从而通过reducer方法来改变state,从而实现页面和状态的通信。而Reducer的形式是(state, action) => newstate。Hooks的useReducer()是这样的:

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

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

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

import React, { 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。

4、useEffect():副作用钩子。

useEffect():副作用钩子,可以用来更好的处理副作用,如异步请求等,Hooks 的 useEffect()也是为函数组件提供了处理副作用的钩子。在类组件中我们会把请求放在 componentDidMount() 里面,在函数组件中我们可以使用 useEffect()。其具体用法如下:

useEffect(() => {},[array])

useEffect() 接受两个参数,第一个参数是要进行的异步操作;第二个参数是一个数组,用来给出Effect的依赖项,只要这个数组发生变化,useEffect() 就会执行。当第二项省略不填时,useEffect()会在每次组件渲染时执行。这一点类似于类组件的 componentDidMount。

例1:通过useEffect()模拟一个异步加载数据。

import React,{ useState,useEffect } from "react";
const AsyncPage = () => {
    const [loading,setLoading] = useState(true)
    useEffect(()=> {
        setTimeout(()=> {
            setLoading(false)
        },5000)
    })
    return (
        loading? <p>loading...</p>: <p>异步请求完成</p>
    )
}
export default AsyncPage;

例2:useEffect()依赖第二项数组变化

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

const AsyncPage = ({ name }) => {
    const [loading,setLoading] = useState(true);
    const [person,setPerson] = useState({});

    useEffect(()=>{
        setLoading(true);
        setTimeout(()=>{
            setLoading(false);
            setPerson({name});
        },2000)
    },[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>
            <br/>
            <button onClick={()=>{changeName('黄蓉')}}>黄蓉</button>
        </>
    )
}

export default PersonPage;

过程是 点击郭靖或黄蓉触发单击事件,使state的值由空字符转换为郭靖或黄蓉,name改变 触发useEffect钩子,重新渲染页面,setLoading变为false;setPerson传入name。return内呈现出所渲染的页面。

四、创建自己的Hooks

以上介绍了四种最常用的react提供给我们的默认React Hooks,有时候我们需要创建我们自己想要的Hooks,来满足更便捷的开发,就是根据业务场景对以上四种Hooks进行组装,从而得到满足自己需求的钩子。

import React,{ 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()时,会根据传入的参数不同而返回不同的状态,然后很简便的应用于我们的页面中。

 类似资料: