React Hooks使用详解

148 阅读5分钟
简介

React Hooks就是函数钩子,对函数型组件进行增强,让函数型组件可以存储状态,可以有处理副作用函数的能力,让开发者在不使用类组件的情况下实现相同的功能。这里的副作用就是不是将数据转化为视图的代码都是副作用函数,比如发送ajax,为dom绑定事件等都是副作用函数,

之所以React Hooks现在大行其道,主要是类组件有以下几个问题:

  • 缺少复用机制,为了复用逻辑增加无实际渲染效果的组件,增加了组件的层级,显示十分臃肿,增加了调试的难度以及运行效率的降低
  • 类组件经常变得很复杂难以维护,我们经常将一组相干的业务逻辑拆分到了多个生命周期中,在一个生命周期函数内存在多个不相干的业务逻辑
  • 类成员方法不能保证this指向的正确性

Hooks就可以很好的解决上述问题,接下里先介绍下基本使用

基本使用

Hooks意为钩子,React Hooks就是一堆钩子函数,React通过这些钩子函数对函数组件进行增强,不同的钩子函数提供了不同的功能。接下来简单的介绍下常见的钩子函数:

useState()

用于为函数组件引入状态,基本用法如下:

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

这里对useState做一个总结:

  • 接受唯一的参数即状态初始值,初始值可以是任意数据类型
  • 返回值为数组,数组中存储状态值和更改状态值的方法,方法约定一set开头,后面加上状态名称
  • 方法可以被调用多次,用以保存不同状态值
  • 参数可以是一个函数,函数返回初始状态值,函数只会被调用一次,用在初始值是动态值的情况
useEffects()

让函数组件拥有处理副作用的能力,类似生命周期函数。可以把useEffect看着是componentDidMount、componentDidUpdate和componentWillUNmount这三个函数的组合。

  • useEffect( () => {}) 会在componentDidMount和componentDidUpdate执行
  • useEffect( () => {},[]) 会在componentDidMount执行
  • useEffect( () => () => {}) 会在componentWillUNmount执行

useEffect解决了这样的两个问题:

  • 按照用途将代码进行分类,将一组相干的业务逻辑归置到了同一个副作用函数中,简化重复代码,是组件内部代码更加清晰。
useReducer()

useReducer是另一种函数组件保存状态的方式,这个函数用法和redux类似,视图触发一个action,这个action会被reducer接收,在reducer中更改state,列个小例子:

import { useReducer } from "react";

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

function Usereducer(){
  const [count,dispath] = useReducer(reducer,0);
  return <div>
    <button onClick={() => dispath({type:'increment'})}>加</button>
    <span>{count}</span>
    <button onClick={() => dispath({type:'decrement'})}>减</button>
  </div>
}

export default Usereducer
useRef()

获取DOM元素,

const box = useRef()
<div ref={box}></div>
//打印box.current 即可获得dom实例

还可以用该钩子函数来保存数据,即时组件重新渲染,保存的数据依然还在,保存的数据被更改不会触发组件重新渲染。

useCallback()

新能优化,缓存函数,使组件重新渲染时得到相同的函数实例

import {useState ,memo} from 'react'

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

  const resetCount = () => {
    setCount(0);
  }
  return (
    <div>
      <span>{count}</span>
      <button onClick={() => setCount(count +1)}>+1</button>
      <Foo resetCount={resetCount}></Foo>
    </div>
  )
}

const Foo = memo(function Foo(props) {
  console.log('Foo重新渲染')
  return <div>Foo组件
    <button onClick={props.resetCount}>resetCount</button>
  </div>
})

export default Call

上面这个例子,每次点击resetCount按钮,都会重新渲染Foo组件,然而我们只是触发了里面的一个函数,不应该重新渲染整个函数。重新渲染Foo的原因就是,在Call组件中,点击+1按钮,会更改count的值,也就会重新渲染整个Call组件,同时也就会每次生成不同的resetCount实例,而我们把改实例传递给了Foo组件,这就会导致Foo的重新渲染。

优化方法就是用useCallback方法重写我们的resetCount函数

const resetCount = useCallback(() => setCount(0), [setCount])

这样只要setCount方法不会更改,resetCount永远只会是同一个实例。

useContext()

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

常规的通过context跨组件传参是这样的:

import { createContext } from "react";

const countContext = createContext();


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

function Foo(){
  return <countContext.Consumer>
    {
      value =>{
        return <div>{value}</div>
      }
    }
  </countContext.Consumer>
}

export default Context

如果改用useContext,代码就大大的简化了

function Foo(){
  const value = useContext(countContext)
  return <div>{value}</div>
}
useMemo()

useMemo的行为类似veu中的计算属性,可以检测某个值的变化,根据变化值计算新值。useMemo会缓存计算结果,如果值没有发生变化,即时组件重新渲染,也不会重新计算,此行为可以有助于避免在每个渲染上进行昂贵的计算。

import {userMemo} from 'react'

const result = userMemo(() =>{
	return result
},[count])
自定义Hooks

自定义Hooks是一个函数,以use开头,是标准的封装和共享逻辑的方式,其实就是逻辑和内置Hooks的组合。

接下来写一个简单的例子:

import {useState,useEffect} from 'react'
import axios from 'axios'

function CustomHooks(){
  const [post,setPost] = useState({});
  useEffect(() => {
    axios.get('https://jsonplaceholder.typicode.com/posts/1')
      .then(res => setPost(res.data))
  },[])
  return (
    <div>
      <div>{post.title}</div>
      <div>{post.body}</div>
    </div>
  )
}

export default CustomHooks

这是一个简单的组件,就是获取接口拿到返回的title和body。这个发送请求的地方,如果其他地方也要用到这个逻辑,就可以封装成一个自定义的Hooks,来实现代码复用的作用:

import {useState,useEffect} from 'react'
import axios from 'axios'

function usePost(){
  const [post,setPost] = useState({})
  useEffect(() => {
    axios.get('https://jsonplaceholder.typicode.com/posts/1')
      .then(res => setPost(res.data))
  },[])
  return [post,setPost]
}

function CustomHooks(){
  const [post] = usePost({});
  
  return (
    <div>
      <div>{post.title}</div>
      <div>{post.body}</div>
    </div>
  )
}

export default CustomHooks