为什么要拥抱React Hooks

2,460 阅读2分钟

什么是 React Hooks

Hooks 是React 16.8 新增的特性,在你不需要写class 组件的情况下,就赋予了函数式组件 state 状态管理及生命周期函数的特性

Hooks的优势

all in 函数式编程

在你编写React应用时,不需要再纠结在 函数式组件、class 类组件、高阶组件、render props 之间的切换

  1. 开发人员首先需要根据页面UI及业务场景,对页面进行抽象和组件拆分,做出人为划分:
    1. 需要逻辑状态的使用class组件
    2. 纯UI展示的使用函数式组件
  2. 哪天需求有变动,需要原来的函数式组件需要状态管理,你需要决定是否需要把状态提升到父级组件 或者 将原函数式组件重写为class组件
  3. 逻辑复用需要考虑使用 高阶组件 还是 render props
  4. 组合优于继承的理念,使用class类组件的继承会使得应用更复杂,难以维护,同时复用性降低

有了Hooks,以上问题统统解决,使用Hooks + 函数式组件可以完成满足所有场景

class 类组件相关逻辑的割裂、不相关逻辑的耦合

  1. class 类组件,各个阶段的声明周期函数,往往把相关的逻辑割裂开 比如你要在 componentDidMount中设置定时器,在 componentWillUnmount中移除定时器;你要在componentDidMount componentDidUpdate 中写重复的逻辑
  2. class 类组件,componentDidMount 钩子函数中往往需要将不相关的逻辑都写在这一个钩子函数中
import React from 'react'
class Timer extends React.Component{
    constructor(props){
        super(props)
        this.state = {
            second: 0,
            count: 0
        }
        this.handleClick = this.handleClick.bind(this)
    }
    handleClick(){
        this.setState(state => {
            return {
                count: state.count+1
            }
        })
    }
    conponentDidMount(){
        document.title = `You clicked ${this.state.count} times`; // 不相关的两个逻辑写到一起

        this.interval = setInterval(() => {
            this.setState(state => {
            return {
                count: state.second+1
            }
        })
        }, 1000)
    }
    componentDidUpdate(){
        document.title = `You clicked ${this.state.count} times`; // 相同的逻辑在conponentDidMount中也写了一遍
    }
    componentWillUnmount(){
        clearInterval(this.interval) // 处理定时器的逻辑被分散到不同生命周期函数中
    }
    render(){
        return (
            <>
                <div>Second: {this.state.second}</div>
                <button onClick={this.handleClick}>count: {this.state.count}</button>
            </>
            )
    }
}

使用hooks,可以将相关逻辑都放在一个useEffect中,不相关逻辑放在各自 useEffect

import { useState, useEffect } from 'react'
function Timer(){
    const [second, setSecond] = useState(0)
    const [count, setCount] = useState(0)

    useEffect(() => {
        document.title = `You clicked ${count} times`;
    },[count])

    useEffect(() => {
        const interval = setInterval(() => {
            setSecond(second => second+1)
            }, 1000)
        return () => clearInterval(interval)
    })
    return (
            <>
                <div>Second: {second}</div>
                <button onClick={setCount(count+1)}>count: .count}</button>
            </>
            )
}

提高状态逻辑的复用性

  1. 在Hooks之前,我们往往通过HOC、render props的方式来完成逻辑复用,但是HOC、render props等方法都会遇到多层嵌套地狱的问题,当嵌套多层时可能会存在props的传值冲突的问题、排查线上问题时某一个state无法快速辨别到底是哪一层高阶组件传进来等问题
  2. 而基于Hooks,我们可以把可复用的状态逻辑抽离到一个函数中作为自定义Hooks,通过多个Hooks的组合完成复杂逻辑共享
  3. 多处复用同一个Hooks时,只是复用Hooks的处理状态的逻辑,每一个Hooks中的状态都是独立的
const { useState, useEffect } from 'react'
function useTimer(){
    const [second, setSecond] = useState(0)
    useEffect(() => {
        const interval = setInterval(() => {
            setSecond(second => second + 1)
        }, 1000)
        return () => clearInterval(interval)
    })
    return second
}

function Timer1(){
    const second = useTimer()
    return <div>second: {second}</div>
}
function Timer2(){
    const second = useTimer()
    return <div>second: {second}</div>
}

组件多状态需求

函数式组件结合hooks,组件的每一次渲染获得的state都是独立的,可以实现多种状态的组件需求,而class类组件中访问的this永远是指向最新的实例状态

function Counter() {
    const [count, setCount] = useState(0);
    const alertMe = () => {
        setTimeout(() => {
            console.log(`You clicked ${count} times`);
        }, 3000);
    }
    return (
        <div>
            <p>You clicked {count} times</p>
            <button onClick={() => setCount(count + 1)}>
                Click me
            </button>
            <button onClick={alertMe}>
                Alert me
            </button>
        </div>
    );
}

先点击Alert me,开启3秒定时器,然后快速点击Click me3次,页面计数累加到3,3s后执行定时器回调,打印的是 You clicked 0 times

参考

Why We Switched to React Hooks