首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

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

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

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

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

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

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

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

ShowCount函数式组件

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

代码语言:javascript
复制
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,则相同的组件会是下面这样。

代码语言:javascript
复制
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组件,我们将引入一种方法,当用户单击标签时,该方法可以更新状态计数。

代码语言:javascript
复制
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上。

代码语言:javascript
复制
this.handleClickEvent = this.handleClickEvent.bind(this);

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

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

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

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

代码语言:javascript
复制
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。

代码语言:javascript
复制
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。

代码语言:javascript
复制
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函数

代码语言:javascript
复制
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块,在逻辑上分离不同的关注点。

代码语言:javascript
复制
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。

代码语言:javascript
复制
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。

代码语言:javascript
复制
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

  • 发表于:
  • 本文为 InfoQ 中文站特供稿件
  • 首发地址https://www.infoq.cn/article/VgDBraSOABW7OtptFEJC
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券