React Hooks概述

Posted 橘猫吃不胖~

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了React Hooks概述相关的知识,希望对你有一定的参考价值。

React Hooks概述

1 为什么会有Hooks

React的组件创建方式,一种是类组件,一种是纯函数组件,并且React团队希望,组件不要变成复杂的容器,最好只是数据流的管道。开发者根据需要,组合管道即可。也就是说组件的最佳写法应该是函数,而不是类

函数组件和类组件的区别:
(1)函数组件没有状态(state),类组件有
(2)函数组件没有生命周期,类组件有(挂载、更新、销毁)
(3)函数组件没有this,类组件有
(4)函数组件更适合做UI展示,类组件更适合做复杂的业务逻辑组件

这就注定,函数组件更适合做UI展示的功能,涉及到状态的管理与切换,我们不得不用类组件或者redux。但我们知道类组件的也是有缺点的,比如,遇到简单的页面,代码会显得很重,并且每创建一个类组件,都要去继承一个React实例;至于Redux,更不用多说,很久之前Redux的作者就说过,“能用React解决的问题就不用Redux”。

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

import React from "react";

class Count extends React.Component 
    constructor(props) 
        super(props);
        this.state = 
            count: 0
        
    
    addCount = () => 
        let count = this.state.count;
        this.setState(
            count: count += 1
        )
    
    render() 
        return (
            <div>
                <p>this.state.count</p>
                <button onClick=this.addCount>1</button>
            </div>
        )
    

可以看出来,上面的代码确实很重。为了解决这种类组件功能齐全却很重,纯函数很轻便却有上文几点重大限制,React团队设计了React Hooks。

React Hooks就是加强版的函数组件,我们可以完全不使用 class,就能写出一个全功能的组件。

2 Hooks的含义

“Hooks”的单词意思为“钩子”。React Hooks 的意思是,组件尽量写成纯函数,如果需要外部功能和副作用,就用钩子把外部代码“钩”进来。而React Hooks 就是我们所说的“钩子”。

那么Hooks要怎么用呢?“你需要写什么功能,就用什么钩子”。对于常见的功能,React为我们提供了一些常用的钩子,当然有特殊需要,我们也可以写自己的钩子。下面是React Hooks为我们提供的默认的四种最常用钩子:
(1)useState()
(2)useContext()
(3)useReducer()
(4)useEffect()

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

3 React Hooks的用法

3.1 useState():状态钩子

纯函数组件没有状态,useState()用于设置和使用组件的状态属性。

示例代码:

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

该函数的返回值为具有2个单元的一维数组,数组的第一个单元存放的是状态属性,指向状态当前值,类似this.state;第二个单元存放的是修改状态属性值的函数,用来更新状态,类似setState。该函数名为set后面加属性名(采用小驼峰命名法)。

示例:使用Hooks重写计数器

const Count = () => 
    const [count, setCount] = useState(0); // 将0设置为count的初始值
    const addCount = () => 
        let newCount = count;
        setCount(newCount += 1);
    
    return (
        <div>
            <p>count</p>
            <button onClick=addCount>1</button>
        </div>
    )

用函数组件实现了一个功能完全一样的计数器,代码看起来更加的轻便简洁,没有了继承,没有了渲染逻辑,没有了生命周期等。这就是hooks存在的意义。

3.2 useContext():共享状态钩子

useContext()可以共享状态,作用是进行状态的分发(React16.x以后的版本支持),避免了使用Props进行数据的传递。

使用方法:

// 第一步:创建全局的Context
const AppContext = React.createContext([初始化参数])

// 第二步:通过全局的Context进行状态值的共享
<AppContext.Provider value= 属性名:>
    <组件1 />
    <组件2 />
</AppContext>

示例:A组件和B组件共享一个状态

const Count = () => 
    const AppContext = React.createContext();
    const A = () => 
        const name = useContext(AppContext);
        return (
            <div>
                我是A组件,我的名字是:name
            </div>
        )
    
    const B = () => 
        const name = useContext(AppContext);
        return (
            <div>
                我是B组件,我的名字是:name
            </div>
        )
    
    return (
        <AppContext.Provider value=name: "橘猫吃不胖">
            <A/>
            <B/>
        </AppContext.Provider>
    )

3.3 useReducer():Action钩子

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

useReducer是useState 的替代方案。首先,关于redux我们都知道,其原理是通过用户在页面中发起action,从而通过reducer方法来改变state,从而实现页面和状态的通信。而Reducer的形式是(state, action) => newstate,返回当前的 state 以及与其配套的 dispatch 方法。。Hooks的useReducer()是这样的:

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

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

在某些场景下,useReducer 会比 useState 更适用,例如 state 逻辑较复杂且包含多个子值,或者下一个 state 依赖于之前的 state 等。并且,使用 useReducer 还能给那些会触发深更新的组件做性能优化,因为可以向子组件传递 dispatch 而不是回调函数。

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

const Count = () => 
    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>1</button>
        </>
    )

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

3.4 useEffect():副作用钩子

useEffect()是副作用的钩子。可以实现特定的功能,如异步请求。作用类似于类组件的componentDidMount。

具体用法:

useEffect(()=>,[array])

“()=>”:回调函数中是要进行的异步操作代码
“[array]”:是useEffect()执行的依赖。当该数组的值发生改变时,回调函数中的代码就会被执行;若该项省略,则组件在每次渲染时回调函数中的代码都会执行。

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

const AsyncPage = () => 
    // 首先设置loading状态为true
    const [loading, setLoading] = useState(true);
    useEffect(() => 
        // 2秒后将loading状态设置为false
        setTimeout(() => 
            setLoading(false);
        , 2000);
    )
    return (
        // 判断loading是否为ttrue,是就显示loading,不是就显示异步请求完成
        loading ? <p>loading...</p> : <p>异步请求完成</p>
    )


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

const AsyncPage = (name) => 
    // 设置loading状态为true
    const [loading, setLoading] = useState(true);
    // 设置person状态为空对象
    const [person, setPerson] = useState();
    useEffect(() => 
        // 首先设置loading为true,2秒后改为false,name改成传过来的参数
        setLoading(true);
        setTimeout(() => 
            setLoading(false);
            setPerson(name);
        , 2000);
    , [name]); // 表示当name修改才会执行useEffect()
    return (
        <>
            loading ? <p>Loading...</p> : <p>person.name</p>
        </>
    )


const PersonPage = () => 
    // 设置初始state为空字符串
    const [state, setState] = useState("");
    const changeName = (name) =>  // 修改name的函数
        setState(name);
    
    return (
        <>
            /*首先将state传给name*/
            <AsyncPage name=state/>
            <button onClick=() =>  // 点击按钮后将张三传给name
                changeName("张三")
            >张三
            </button>
            <button onClick=() => 
                changeName("李四")
            >李四
            </button>
        </>
    )

4 创建自己的Hooks

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

用户自定义的Hooks:
A、命名的要求:用use开头,后跟名称(首字母大写)
B、作用:根据具体业务的需求,对Hooks中默认的钩子函数进行封装,使代码的结构更加清晰,便于使用和维护

示例:封装自己的Hooks

// 封装自己的Hooks
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) =>  // 只进行UI展示
    const [loading, person] = usePerson(name)
    return (
        <>
            loading ? <p>Loading...</p> : <p>person.name</p>
        </>
    )


const PersonPage = () => 
    const [userName, setUserName] = useState('')

    const changeName = (name) => 
        setUserName(name);
    
    return (
        <>
            <AsyncPage name=userName/>
            <br/>
            <button onClick=() => 
                changeName('张三')
            >张三
            </button>
            <br/>
            <button onClick=() => 
                changeName('李四')
            >李四
            </button>
        </>
    )

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

以上是关于React Hooks概述的主要内容,如果未能解决你的问题,请参考以下文章

React Hooks - 将状态设置为初始状态

React Hooks概述

React-hooks

react hooks之useDebounce useThrottle

轻松渲染优化:使用React Hooks进行state跟踪

GraphQL Apollo React Hooks 查询根据初始化顺序返回 null