为什么React 需要hook,相比类组件,优势在哪里?

300 阅读3分钟

自言自语

我想大部分人现在学react都会直接上手hook开发,因为很简单,上手比较容易,而且目前业界也都转向hook开发了。但从我学react到现在其实还没有认真思考学习过,react团队为啥会出一个hook,它的优势是什么?什么情况下应该使用类组件?

处理生命周期

在class组件中,处理组件的state需要使用比较多的生命周期钩子,比如componentDidUpdateshouldComponentUpdatecomponentDidMountcomponentWillUnMount等等,你要在每个函数中去处理逻辑,相当的麻烦,而且处理state更新的时候还要注意引用类型地址的问题。比如

class ComponentA extends React.Component {
    componentDidUpdate(prevProps: Props) {
        if (prevProps.obj !== this.props.obj) {
            // do something
            // 这个过程是一定会执行,因为引用类型地址没变
            // 处理的方式一般是用loadsh的isEqual去处理,但这很容易漏,造成组件性能问题甚至性能问题
        }
    }


    render() {
        return (
            <div>
                ComponentA
            </div>
        )
    }
}

相反,用hook的话就使用useStateuseEffect这两个hook就可以完成很多事了,大大简化了代码的书写。

编写代码的问题

在类组件中会存在this丢失的情况,但这种倒不是框架的设计问题,js本身有这个问题,所以在类型组件中药使用函数,得使用bind绑定函数或者使用剪头函数,也挺麻烦的

class ComponentA extends React.Component {
    logMessage() {
        console.log('log message from ComponentA');
    }
    
    logMessageA = () => {
        console.log('log messageA from ComponentA');
    }
    
    render() {
        return (
            <div onClick={this.logMessage.bind(this)}>
                ComponentA
            </div>
            <div onClick={this.logMessageA}>
            </div>
        )
    }
}

这方面函数式+hook的形式就不需要考虑这么多,不过在useEffect的依赖项中增加对象依旧不是一个优雅的选择。

代码复用的问题

在前端开发中经常会遇到要开发高复用组件的场景,需要用到高阶组件,而class组件在使用高阶组件的过程中会有组件嵌套的问题,逻辑更加复杂,容易形成屎山。

这里的实例只用到了一个logger,但是现实开发中,需求是在叠加的,

class组件
const useLogger = (Component) => {
    class LoggerComponent extends React.Component {
        componentDidMount() {
            console.log('component dit mount');
        }
        
        componentWillUnMount() {
            console.log('component will un mount');
        }
        
        render() {
            return <Component {...this.props}/>;
        }
    }
    
    return LoggerComponent;
}

class DemoComponent extends React.Component {
    render() {
        return <div>demo></div>
    }
}

const loggerComponent = useLogger(DemoComponent);

hook
import { useEffect } from 'react';

const useLogger = () => {
  useEffect(() => {
    console.log('component dit mount');
    return () => {
      console.log('component will un mount');
    };
  }, []);
};

function MyDemoComponent() {
  useLogger();
  return <div>demo</div>;
}

需要用到类组件的场景

正常的前端ui界面搭建,我想直接用hook就够了,不过在一些特殊的开发场景,类的优势会体现出来。比如你正常做一个绘图的web端软件。

你需要提供很多种绘画的方式,比如曲线、直线、圆圈、文字等,这些通常用canvas做,涉及到3d的用webgl底层做。在做这部分设计类的软件的时候,你需要一个类似tool的组件。

classDiagram

DrawTool <|-- DrawStraightLineTool
DrawTool <|-- DrawCurveTool

class DrawTool {
   type: ToolType.DrawStraightLine;
   DrawType: drawType;
   draw();
   stop();
   beforeLeaveTool();
   afterLeaveTool();
   active();
}

class DrawStraightLineTool {
    drawType: DrawType.Straight;
    draw();
    stop();
    ....()
}

class DrawCurveTool {
    drawType: DrawType.Curve;
    draw();
    stop();
    ....()
}

在这个例子里你需要有一个基类DrawTool和两个子类DrawStraightLineToolDrawCurveTool,通过多态的方式来实现绘制过程中的一些交互。在这个场景下,class组件就能发挥它的优势了,这种工具类的架构,class组件确实比函数式要来的方便,不过对于维护代码的人的要求要更高点。