Skip to content
微信公众号

React Hooks

React Hooks介绍

React Hooks是对函数型组件进⾏增强, 让函数型组件可以存储状态, 可以拥有处理副作⽤的能⼒。让开发者在不使⽤类组件的情况下, 实现相同的功能。从React16.8开始,react官方推荐使用函数创建组件,当然也不会放弃对类组件的支持。

什么是副作用呢?在一个组件当中,只要不是把数据转换成数据的代码,就是副作用。例如获取DOM元素、为DOM元素添加事件、设置定时器以及发送ajax请求,这都属于副作用代码。在类组件中,我们通常使用生命周期函数来处理副作用,在函数式组件中,我们就需要使用hooks来处理副作用。

类组件的不足

React中类组件的不足就是hooks要解决的问题。

  1. 缺少逻辑复⽤机制:为了复⽤逻辑增加⽆实际渲染效果的组件,增加了组件层级显示⼗分臃肿。增加了调试的难度以及运⾏效率的降低
  2. 类组件经常会变得很复杂难以维护:将⼀组相⼲的业务逻辑拆分到了多个⽣命周期函数中。在⼀个⽣命周期函数内存在多个不相⼲的业务逻辑
  3. 类成员⽅法不能保证this指向的正确性

React Hooks使用

Hooks 意为钩⼦, React Hooks 就是⼀堆钩⼦函数, React 通过这些钩⼦函数对函数型组件进⾏增强, 不同的钩⼦函数提供了不同的功能。

  • useState()
  • useEffects()
  • useReducer()
  • useRef()
  • useCallback()
  • useContext()
  • useMemo()

useState

功能是让函数型组件保存状态,在我们的认知中,函数中的变量执行完后会被释放掉,所以函数型组件不可以保存状态数据。 现在有useState就可以保存状态数据了,内部使用闭包来保存状态数据。

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

function App(){
    const [count,setCount] = useState(0);
    return <div>
        <span>{count}</span>
        <button onClick={()=>setCount(count+1)}>+1</button>
    </div>;
}

当状态值修改后,组件会重新渲染显示最新的状态值。

  1. 接收唯⼀的参数即状态初始值. 初始值可以是任意数据类型.
  2. 返回值为数组. 数组中存储状态值和更改状态值的⽅法. ⽅法名称约定以set开头, 后⾯加上状态名称.
  3. ⽅法可以被调⽤多次. ⽤以保存不同状态值.
  4. 参数可以是⼀个函数, 函数返回什么, 初始状态就是什么, 函数只会被调⽤⼀次, ⽤在初始值是动态值的情况

设置状态值⽅法的参数可以是⼀个值也可以是⼀个函数,设置状态值⽅法的⽅法本身是异步的。

useReducer

useReducer是另⼀种让函数组件保存状态的⽅式,它的状态保存在特定的地方,如果组件想要更改其状态,要通过dispatch来触发一个action,这个action会被reducer函数接收到,在reducer内部会判断函数类型是什么,从而决定状态如何处理,最后通过返回值的方式来更新状态。

jsx
import React,{useReducer} from 'react';

function reducer(state,action){
    switch(action.type){
        case 'increment':
            return state + 1;
    }
}

function App(){
    const [count,dispatch] = useReducer(reducer,0);
    return <div>
        <span>{count}</span>
        <button onClick={()=>dispatch({type:'increment'})}>+1</button>
    </div>;
}

useContext

用于在跨组件层级获取数据时简化获取数据的代码。

jsx
import { createContext, useContext } from 'react';

const countContext = createContext();

function App(){
    return <countContext.Provider value = {100}>
        <Foo />
    </countContext.Provider>
}

function Foo(){
    const count = useContext(countContext);
    return <div>{count}</div>
}

useEffect

它的作用是让函数型组件拥有处理副作用的能力,类似生命周期函数,在类组件中使用生命周期函数来处理副作用,在函数型组件中使用useEffect处理副作用。

useEffect执行时机

可以把 useEffect 看做 componentDidMount, componentDidUpdate 和 componentWillUnmount 这三个函数的组合,也就是会在组件挂载之后执行,会在组件数据更新完成之后执行,会在组件卸载之前执行。

它的语法是:

  • useEffect(() => {}) => componentDidMount, componentDidUpdate,也就是会在组件挂载和更新完成之后执行
  • useEffect(() => {}, []) => componentDidMount ,在组件挂载完成后执行一次
  • useEffect(() => () => {}) => componentWillUnMount,传入的回调函数有一个返回值,该返回值是一个函数,该返回值函数会在组件卸载之前执行,做清理工作。

useEffect使用方法

  1. 为window对象添加滚动事件
  2. 设置定时器让count数值每隔⼀秒增加1
jsx
import React,{useEffect,useState} from "react";
import ReactDOM from "react-dom";

function App(){
    function onScroll(){
        console.log('页面发生滚动了');
    }

    useEffect(()=>{
        window.addEventListener('scroll',onScroll);
        return ()=>{
            window.removeEventListener('scroll',onScroll);
        }
    },[])

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

    useEffect(()=>{
        const timerId = setInterval(()=>{
            setCount(()=>count + 1);
        },1000);
        return ()=>{
            clearInterval(timerId);
        }
    },[]);
    return <div>
        <span>{count}</span>
        <button onClick={()=>ReactDOM.unmountComponentAtNode(document.getElementById('root'))}>卸载组件</button>
    </div>;
}

export default App;

useEffect 解决的问题

通过useEffect处理副作用,相比类组件有什么优势呢?

  1. 按照⽤途将代码进⾏分类 (将⼀组相⼲的业务逻辑归置到了同⼀个副作⽤函数中) :由于useEffect可以多次调用,我们可以按照用途,把不同用途的代码放在不同的useEffect中,进行分类。
  2. 简化重复代码, 使组件内部代码更加清晰:一般情况下组件挂载要做的和更新之后做的事情是一样的,使用useEffect将两个api替换成一个api,简化代码。

useEffect数据监测

只有指定数据发生变化时触发effect。

js
useEffect(()=>{
    document.title = count;
},[count]);

useEffect结合异步函数

useEffect中的参数函数不能是异步函数, 因为useEffect函数要返回清理资源的函数, 如果是异步函数就变成了返回Promise

js
useEffect(()=>{
    (async ()=>{
        await axios.get()
    })()
})

useMemo

  • useMemo 的⾏为类似Vue中的计算属性, 可以监测某个值的变化, 根据变化值计算新值
  • useMemo 会缓存计算结果. 如果监测值没有发⽣变化, 即使组件重新渲染, 也不会重新计算. 此⾏为可以有助于避免在每个渲染上进⾏昂贵的计算
jsx
import {useMemo} from 'react';

const result = useMemo(()=>{
    //如果count值发生变化此函数重新执行
    return result;
},[count])

memo方法

该方法的作用是在组件重新渲染之前,看一下组件中的数据有没有发生变化,如果组件中的数据没有变化,它就阻止当前组件进行重新渲染,它是用来做性能优化的。类似于类组件中的PureComponent和shouldComponentUpdate生命周期函数,如果我们要在类组件实现相同的功能,我们可以让类组件继承PureComponent,也可以在类组件的shouldComponentUpdate生命周期函数中判断数据有没有发生变化,没有return false阻止组件重新渲染。

jsx
import React, {memo} from 'react';

function Counter(){
    return <div></div>;
}
export default memo(Counter);

useCallback

它的作用是缓存函数, 使组件重新渲染时得到相同的函数实例,它也是用来做性能优化的。

jsx
import React, {useCallback} from 'react';

function Counter(){
    const [count,setCout] = useState(0);
    const resetCount = useCallback(()=>setCount(0),[setCount]);
    return <div>
        <span>{count}</span>
        <button onClick={()=>setCount(count+1)}>+1</button>
        <Test resetCount={resetCount}/>
    </div>
}
jsx
import {memo} from 'react';
function Test(props){
    console.log('Test re-render');
    return <div>
        Test
        <button onClick={props.resetCount}>reset</button>
    </div>
}

useRef

它用来获取DOM元素对象,useRef创建的对象有current属性,该属性就是你要获取的DOM对象。

jsx
import React, {useRef} from 'react';

function App(){
    const username = useRef();
    const handler = ()=>console.log(usename); //{current:input}
    return <input ref={username} onChange={handler}/>
}

useRef还有另外一个功能就是保存数据,保存的数据有一个特点就是跨组件周期的。即使组件重新渲染,保存的数据仍然还在,保存的数据被更改不会触发组件重新渲染。

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

function App(){
    const [count,setCount] = useState(0);
    let timerId = useRef();
    useEffect(()=>{
        timerId.current = setInterval(()=>{
            setCount(count=>count+1)
        },1000)
    },[])
    const stopCount=()=>{
        console.log(timerId)
        clearInterval(timerId.current)
    }
    return <div>
        {count}
        <button onClick={stopCount}>停止</button>
    </div>;
}
export default App;

自定义hook函数

自定义hook就是自己创建的hook函数,⾃定义 Hook 是标准的封装和共享逻辑的⽅式,意思是在组件的内部,有哪些逻辑是共享的,其他组件也可能使用到的逻辑,这时候就可以把逻辑写到自定义hook当中,在哪个组件想要使用逻辑就调用自定义hook即可。

⾃定义 Hook 是⼀个函数, 其名称以 use 开头。⾃定义 Hook 其实就是逻辑和内置 Hook 的组合。

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

function useGetPost(){
    const [post,setPost] = useState({});
    useEffect(()=>{
        axios.get('http://www.xxx.com')
        .then(response=>setPost(response.data));
    },[]);
    return [post,setPost]
}

function App(){
    const [post,setPost] = useGetPost();
    return <div>
        <div>{post.title}</div>
        <div>{post.body}</div>
    </div>
}
jsx
import React,{useState,useEffect} from 'react';

function useUpdateInput(initialValue){
    const [value,setValue] = useState(initialValue);
    return {
        value,
        onChange:event=>setValue(event.target.value)
    }
}
function App(){
    const usernameInput = useUpdateInput('');
    const passwordInput = useUpdateInput('');
    const submitForm = event=>{
        event.preventDefault();
        console.log(usernameInput.value);
        console.log(passwordInput.value);
    }
    return <form onSubmit={submitForm}>
        <input type="text" name="username" {...usernameInput} />
        <input type="password" name="password" {...passwordInput} />
        <input type="submit" />
    </form>
}

React路由Hooks

react-router-dom路由提供了4个钩子函数

  • useHistory
  • useLocation
  • useRouteMatch
  • useParams

这些钩子函数的作用就是获取相关的路由信息, 当进入某个路由组件的时候,组件的props下面就会多出几个对象,比如history、location、match,这些钩子函数的作用就是为了获取这几个对象信息。

本站总访问量次,本站总访客数人次
Released under the MIT License.