写点什么

使用 React Hooks 代替类的 6 大理由

2020 年 10 月 12 日

使用React Hooks代替类的6大理由

React hooks 已经出来有一段时间了,但是许多 React 开发人员对它们的态度并不是很积极。我发现这背后主要有两个原因。第一个原因是许多 React 开发人员已经参与了一个大型项目,需要付出巨大的努力才能迁移整个代码库。另一个原因是大家对 React 类已经很熟悉了。有足够经验的话,继续使用类会感到更自在。


在本文中,我们将探讨考虑使用 React Hooks 的六个原因。


1. 扩展函数式组件时,不必将其重构为类组件


经常会有这种情况,那就是一个 React 组件从一个函数式组件开始开发,一开始这个函数式组件只依赖 props,后来演变为具有状态的类组件。从函数式组件更改为类组件需要一些重构工作,具体取决于组件的复杂程度。


使用 React Hooks 时,由于函数式组件具有进入状态的能力,因此重构工作会非常少。来看以下示例,这是一个哑组件,它会显示一个带有计数的标签。


export function ShowCount(props) {  return (    <div>       <h1> Count : {props.count} </h1>    </div>  );}
复制代码


ShowCount 函数式组件


假设我们需要通过点击鼠标来增加计数,并假设这只会影响这一个组件。第一步,我们需要将状态引入组件。我们看一下如何使用基于类的方法。


export class ShowCount extends React.Component {  constructor(props) {    super(props);    this.state = {      count: 0    };  }  componentDidMount() {    this.setState({      count: this.props.count    })  }  render() {    return (      <div>         <h1> Count : {this.state.count} </h1>      </div>    );  }  }
复制代码


引入状态后的 ShowCount 组件


如果使用 hooks,则相同的组件会是下面这样。


export function ShowCount(props) {  const [count, setCount] = useState();  useEffect(() => {    setCount(props.count);  }, [props.count]);  return (    <div>      <h1> Count : {count} </h1>    </div>  );}
复制代码


带 hooks 的 ShowCount 组件


2. 你不必再担心“this”了


人类和机器都会因为类而困惑


上面这句话来自React文档。造成这种混乱的原因之一是 this 关键字。如果你熟悉 JavaScript,就会知道 JavaScript 中的 this 与其他语言并不完全一样。但在 React Hooks 这边,你完全不必操心 this 了。这对初学者和经验丰富的开发人员来说都是有益的。



分别使用 hooks 与类访问状态的对比


根据上面的示例,你可以看到访问状态时我们不再需要使用“this”。这样大家就都不会感到困惑了。


3. 不再有方法绑定


现在,对于前面提到的那个 ShowCount 组件,我们将引入一种方法,当用户单击标签时,该方法可以更新状态计数。


export class ShowCount extends React.Component {  constructor(props) {    super(props);    this.state = {      count: 0    };    this.handleClickEvent = this.handleClickEvent.bind(this);  }  componentDidMount() {    this.setState({      count: this.props.count    });  }  handleClickEvent() {    this.setState({count: this.state.count + 1});  }  render() {    return (      <div>        <h1 onClick={this.handleClickEvent} > Count : {this.state.count} </h1>      </div>    );  }}
复制代码


具有 Click 事件处理程序的 ShowCount 组件


我们引入了 handleClickEvent 方法。要使用它,首先我们必须将其绑定到该组件的 this 上。


this.handleClickEvent = this.handleClickEvent.bind(this);
复制代码


我们必须这样做,因为执行方法时的执行上下文是不一样的。对于初学者来说,这可能有点难以理解。


除了绑定所有方法之外,还有一些语法提案可以解决这个问题。例如,我们可以将这个函数重写为一个箭头函数。


handleClickEvent = () => {  this.setState({count: this.state.count + 1});}
复制代码


让我们看看如何使用 hooks 实现相同的功能。


export function ShowCount(props) {  const [count, setCount] = useState();  useEffect(() => {    setCount(props.count);  }, [props.count]);  function handleClickEvent() {    setCount(count + 1);  }  return (    <div>      <h1 onClick={handleClickEvent}> Count : {count} </h1>    </div>  );}
复制代码


hooks 中带有事件处理程序的 ShowCount 组件


如你所见,我们只添加了这个函数。另外你可能会注意到,当我们使用事件处理程序时,已经在模板中删掉了 this。


onClick={ handleClickEvent }
复制代码


4. 更容易分离逻辑与 UI,从而使两者都更加可重用


使用 hooks 时,逻辑和 UI 更容易分离。无需 HOC 或渲染 props。hooks 用更少的样板和更直观的 UI 和逻辑组合来优雅地做到这一点。


当使用Bit之类的工具和平台共享组件时,这种“优雅的分离”尤其重要,因为每个(独立共享的)组件在不同应用程序之间都会更容易理解、维护和重用。



Bit.dev 上的共享 React 组件


5.将相关逻辑放在同一位置


复杂的组件会变得难以理解


使用基于类的方法,我们会有不同的生命周期方法,例如 componentDidMount 和 componentDidUpdate 等。让我们考虑一种情况,就是在 componentDidMount 中订阅服务 A 和 B,然后在 componentWillUnmount 中取消订阅它们。随着时间的流逝,两种生命周期方法中都会包含许多逻辑,并且很难跟踪挂载与卸载过程中哪些部分是相关联的。


为了演示这一点,我们来创建一个基于 RxJs 的服务来获取计数。我们将使用这个服务来更新 ShowCount 示例中的计数。请注意,由于不再需要在 click 事件中更新组件,我们将删除 handleClickEvent。


import { Subject } from "rxjs";export function getCounts() {  const subject = new Subject();  let count = 0;  const interval = setInterval(() => {    if (count > 10 || subject.isStopped) {      clearInterval(interval);      subject.complete();    }    subject.next(count++);  }, 1000);  return subject;}
复制代码


getCounts 函数


import { getCounts } from "./reactive-service";export function ShowCount(props) {  const [count, setCount] = useState();  useEffect(() => {    const countServiceSubject = getCounts();    countServiceSubject.subscribe((count) => {      setCount(count);    });    return () => {      countServiceSubject.unsubscribe();    };  }, []);  return (    <div>      <h1> Count : {count} </h1>    </div>  );}
复制代码


带有 Effect hook 中 getCounts 方法的 ShowCount 组件


你可以看到在 useEffect 内部,我们包括了订阅以及相应的取消订阅逻辑。同样,如果我们需要引入更多的服务订阅或不相关的逻辑,则可以添加多个 useEffect 块,在逻辑上分离不同的关注点。


import { getCounts } from "./reactive-service";export function ShowCount(props) {  const [count, setCount] = useState();  const [secondCount, setSecondCount] = useState(0);  useEffect(() => {    const countServiceSubject = getCounts();    countServiceSubject.subscribe((count) => {      setCount(count);    });    return () => {      countServiceSubject.unsubscribe();    };  }, []);  useEffect(() => {    setSecondCount(secondCount + 1);  }, []);  return (    <div>      <h1> Count : {count} </h1>      <h1> Second Count: {secondCount} </h1>    </div>  );}
复制代码


多个 useEffect 块可分离不相关的逻辑


6. 在组件之间共享状态逻辑


使用基于类的方法时,我们很难在组件之间共享状态逻辑。考虑两个组件,这两个组件都必须从两个不同的数据源获取、排序和显示数据。即使两个组件具有相同的功能,它们之间也很难共享逻辑,因为这些组件具有不同的源和状态。


虽然我们可以使用渲染props高阶组件来解决这个问题,但由于我们必须重构组件,这会引入额外的成本,到头来会变得更麻烦。


React Hooks 又是怎么做的呢?


使用自定义 React Hooks,你可以提取这些可重用的状态逻辑并分别测试它们。


我们可以从 ShowCount 示例中提取一个自定义 hook。


import { useState, useEffect } from "react";export function useCount(serviceSubject) {  const [count, setCount] = useState();  useEffect(() => {    serviceSubject.subscribe((count) => {      setCount(count);    });    return () => {      serviceSubject.unsubscribe();    };  }, [serviceSubject]);  return [count, setCount];}
复制代码


自定义 hook 来共享状态逻辑


使用上面的自定义 hook,我们可以像下面这样重写 ShowCount 组件。注意,我们必须将数据源作为参数传递给这个自定义 hook。


import { useCount } from "./use-count";export function ShowCount(props) {  const [count, setCount] = useCount(props.serviceSubject);  useEffect(() => {    setCount(-1);  }, [setCount]);  return (    <div>      <h1> Count : {count} </h1>    </div>  );}
复制代码


ShowCount 组件,带有数据源参数


请注意,我们在父组件,而不是ShowCount组件中调用getCounts。否则,serviceSubject每次运行ShowCount时都将有一个新值,而我们将无法获得预期的结果。


结论


切换到 React Hooks 的理由有很多,但我已经提到了其中一些最令人信服的原因,这些足够让我改用 React Hooks 了。如果查看官方文档,你会发现 React Hooks 有很多有趣的功能。请大家也谈一谈自己的 React Hooks 之旅吧。


你可以在此处找到完整的源代码。


原文链接:


https://blog.bitsrc.io/6-reasons-to-use-react-hooks-instead-of-classes-7e3ee745fe04


2020 年 10 月 12 日 17:371565

评论

发布
暂无评论
发现更多内容

程序员的晚餐 | 6 月 4 日 最好吃的土豆

清远

架构师训练营-每周学习总结1

水边

极客大学架构师训练营

UML练习1 食堂就餐卡系统设计「架构师训练营」

Young

利其器

宋胖子

IDEA

不可不知的 7 个 JDK 命令

武培轩

Java 程序员 jdk 后端 JVM

架构文档

陈皮

架构 极客大学架构师训练营

食堂就餐卡系统设计-uml练习

森林

程序员的晚餐 | 6 月 5 日 爆炒鱿鱼

清远

美食

架构师训练营第一周学习总结

王鑫龙

极客大学架构师训练营

【ARTS打卡】Week02

Rex

因为 MongoDB 没入门,我丢了一份实习工作

沉默王二

mongodb

软件架构第一章总结

itrickzhang

Java 25周年:波澜壮阔的25年

北风

「Java 25周年」

架构师训练营-命题作业1

水边

极客大学架构师训练营

Flink源码分析之Flink startupMode是如何起作用的

shengjk1

flink flink 消费 kafak 实时计算 flink源码 flink源码分析

优秀架构师具备的能力

阿飞

极客大学架构师训练营

如何用一台 MacBook 创造高额年化收益 | ETH2.0 Staking 教程

陈东泽 EuryChen

区块链 Ethereum

[ARTS打卡] week 02

Mau

ARTS 打卡计划

【架构师训练营-作业-1】食堂就餐卡系统设计

小动物

系统设计 极客大学架构师训练营 作业

Flink源码分析之FlinkConsumer是如何保证一个partition对应一个thread的

shengjk1

flink flink 消费 kafka 实时计算 flink源码分析

教你动手写UDP协议栈

Rice嵌入式开发技术分享

TCP udp 协议栈

程序员摆地摊?你别痴心妄想了,还不如当「在地青年」呢

非著名程序员

程序员 提升认知 职业规划 认知提升

repo 导出本地 git tag 给他人

zqb-all

git

Flink源码分析之Flink是如何kafka读取数据的

shengjk1

flink flink 消费 kafka flink源码分析 flink消费kafka源码解析

极客时间-架构师培训-1期作业

Damon

ARTS 第 1 周

乌拉里

ARTS 打卡计划

2020年6月7日 接口、lambda表达式与内部类

瑞克与莫迪

《OKR工作法》读书笔记

大饼土博

读书笔记 管理 OKR

人人都是产品经理

二鱼先生

产品经理 个人品牌 职场成长 产品思维

Flink源码分析之Flink 自定义source、sink 是如何起作用的

shengjk1

flink flink源码 flink源码分析 flink自定义source flink自定义sink

Flink源码分析之-如何保存 offset

shengjk1

飞猪Flutter技术演进及业务改造的实践与思考

飞猪Flutter技术演进及业务改造的实践与思考

使用React Hooks代替类的6大理由-InfoQ