使用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:37 1403

评论

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

架构师训练营 week3 总结

陈皓07

第二周作业

Kenny

架构师训练营 - 第三周作业

咖啡

架构师训练营第 1 期第三周课后练习题

郑凯元

架构师训练营第 1 期

Week 3 作业 02

Croesus

架构训练营 - 第3周课后作业 - 学习总结

Pudding

第三周总结

积极&丧

「架构师训练营第 1 期」

Week_03 作业

golangboy

架构师训练营 - 第 3 周课后作业(1 期)

Pudding

第三周 代码重构 学习总结

应鹏

架构师训练营第一期

第三周总结

fmouse

架构师训练营第 1 期

架构师训练营第一期——第三周作业

tao

Week 3 學習總結 (Christy 羅同學) - Design patterns

--------世界中心---------

架構師 設計模式 Design Patterns

链表转换为二叉排序树、反应式编程 RxSwift和RxCocoa 、区块链hyperledger环境搭建、环境架构、John 易筋 ARTS 打卡 Week 20

John(易筋)

响应式编程 ARTS 打卡计划 hyperledger 链表转为二叉排序树 chmod

架构师训练营第一期——第三周总结

tao

第三周学习总结

熊桂平

架构师训练营第 1 期

Singleton Pattern & Composite Pattern

第三周 作业一

mm马

「架构师训练营第 1 期」

架构师训练营Week03作业

IT老兵重开始

架构师训练营第一期

【架构师训练营第 1 期 03 周】 学习总结

Bear在挨踢

架构师训练营第 1 期

Week_03 学习总结

golangboy

「架构师训练营第 1 期」

架构师训练营第 1 期 02 周 作业

Geek_a01290

架构师训练营第 1 期 「架构师训练营第 1 期」

第三周课后练习 - 作业 1

致星海

第三周作业

Kenny

第三周

等燕归

Week 3 作業一 : Design Patterns (Christy 羅同學)

--------世界中心---------

JavaScript 架構師 設計模式 Design Patterns

第三周 学习总结

mm马

架构师训练营第 1 期

第三周作业

熊桂平

架构师训练营第 1 期

架构师训练营第三周命题作业

一马行千里

命题作业 架构师训练营第 1 期

第三周课后练习 - 作业 2

致星海

设计模式及相关应用案例

garlic

架构师训练营第 1 期

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