React hooks: 一种新的开发方式

栏目: IOS · Android · 发布时间: 5年前

内容简介:如果使用React的话,你可能就知道那些所谓的注意:hook刚刚发布,API可能会随时变化,建议查看https://react tjs.org/docs/hooks-intro.html的官方文档,并关注先看两个例子:

如果使用React的话,你可能就知道那些所谓的 hook 了。索菲·阿尔珀特和丹·阿布拉莫夫在今年的React大会上正式宣布了这一消息。他们的演讲视频可以在 这里 看到。我和许多人一样,对这个新特性很感兴趣。这篇文章基本上总结了我关于React hook的一点个人思考。

注意:hook刚刚发布,API可能会随时变化,建议查看https://react tjs.org/docs/hooks-intro.html的官方文档,并关注 RFC

hooks 是什么

先看两个例子:

import React, { useState } from 'react';

function Counter() {
  const initialValue = 0;
  const [ count, setCount ] = useState(initialValue);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

首先, hooks 适用于无状态组件。它们在用class定义的React组件中没有意义(事实上也不会起作用)。在上面的例子中, useState 是钩子。它的作用是使我们的Counter组件有状态。换句话说,我们拥有与使用class相同的组件状态功能。 useState 接受状态的初始值,并返回一个包含两个元素的数组。第一个是count,第二个是我们可以用来改变count的函数。

返回数组而不是对象的决定非常好。这样我们可以毫不费力地正确命名对象。

想象一下有如下一个钩子:

const { value: count, changeValue: setCount } = useState(initialValue);

看起来比想象的还要冗长。

到目前为止,定义为函数的React组件与定义为class的组件相比欠缺两样东西——管理组件状态和生命周期函数。 useState 用于管理组件状态,还有一个钩子叫做 useEffect ,它实际上是生命周期方法的替代者。

import React, { useEffect } from 'react';

function Example() {
  useEffect(() => {
    document.title = `The component is rendered`;
    return () => {
      document.title = `The component is removed from the DOM`;
    }
  });

  return <p>Hello world</p>;
}

当页面渲染完成后,执行传递给 useEffect 的函数,它类似于 componentDidMount 。当组件从页面中移除时,将调用我们在其中返回的函数,类似于 componentWillUnmount 。因为所有这些都是在函数内部定义的,我们可以访问props 和state。这意味着我们可以覆盖 componentDidUpdate 提供给我们的内容。

优势

如果我们使用钩子,编写的代码就会更少,代码可读性更好。我们只写函数,不写类。在构造函数中没有使用关键字this,也没有奇怪的绑定。React组件以声明式的方式编写,几乎没有分支,而且更容易跟踪。

class Counter extends React.Component {
  constructor(props) {
    super(props);

    this.state = { count: 0 };
    this.onButtonClicked = this.onButtonClicked.bind(this);
  }
  onButtonClicked() {
    this.setState({ count: this.state.count + 1 });
  }
  render() {
    const { count } = this.state;

    return (
      <div>
        <p>You clicked {count} times</p>
        <button onClick={this.onButtonClicked}>
          Click me
        </button>
      </div>
    );
  }
}

它与之前定义的Counter函数等价。里面有三个方法,所以我们需要看懂这些方法才能完全理解发生了什么。bind看起来很奇怪,但是我们必须这样做,由于性能原因,我们不能将.bind留在render方法中。总的来说,在使用class编写React组件时,我们必须写一些模板代码。我们采用钩子的写法重写Counter:

function Counter() {
  const initialValue = 0;
  const [ count, setCount ] = useState(initialValue);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

用更少的代码实现同样的事情。

但对我来说更重要的是两件事——组件变得更容易阅读,状态逻辑更容易共享。

假设我想使用相同的Counter逻辑,但是使用不同的View。如果我们决定使用类,我们可能会使用函数作为子模式,就像这样:

class Counter extends React.Component {
  constructor(props) {
    super(props);

    this.state = { count: 0 };
    this.onButtonClicked = this.onButtonClicked.bind(this);
  }
  onButtonClicked() {
    this.setState({ count: this.state.count + 1 });
  }
  render() {
    const { count } = this.state;
    const { children } = this.props;

    return children({ count, add: onButtonClicked });
  }
}

然后多次使用相同的计数器组件:

function AnotherCounter() {
  return (
    <Counter>
      {
        ({ count, add }) => (
          <div>
            <p>You clicked {count} times</p>
            <button onClick={add}>
              Click me
            </button>
          </div>
        )
      }
    </Counter>
  )
}

现在我们在组件树中又多了一个div,随着代码变得复杂,早晚会遇到 嵌套层级过深 的问题。传递一个表达式作为children不是最自然的事情。另一方面,使用简单的JavaScript函数感觉很正常。

function useCounter(initialValue) {
  const [value, setCount] = useState(initialValue)

  return {
    value,
    increase: () => setCount(value + 1),
  }
}
export default function CounterA() {
  const counter = useCounter(0)

  return (
    <div>
      <p>You clicked {counter.value} times</p>
      <button onClick={counter.increase}>Click me</button>
    </div>
  )
}

使用 hooks 可以将状态逻辑提取到一个简单的JavaScript函数中,该函数只是 useStateuseEffect 等基本钩子的组合。

一些困惑

到目前为止,我们看到了 hooks 带来的好处。同时也会带来一些问题:

第一件事是编写React函数组件的思维方式。过去我们认为它们是简单、无状态的函数,只负责渲染内容。现在我们依然可以让它们这样,但是如果 hooks 变成了新的React组件开发方式,我们就不能继续说,如果它是一个函数,它没有状态,它纯粹是渲染的东西。特别是在使用 useEffect 钩子时,我们传递了一个函数,该函数可能会执行异步任务。这意味着,即使返回一个结果,定义为函数的React组件仍然是可变的。例如:

function FriendStatus(props) {
  const [isOnline, setIsOnline] = useState(null);

  useEffect(function onRender() {
    ChatAPI.subscribeToFriendStatus(
      props.friend.id,
      status => setIsOnline(status.isOnline)
    );
  });

  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}

请注意 useEffect 接收一个在将来某个时刻执行的 onRender 函数。我们过去认为React组件会返回结果。我认为令人困惑的是 useEffect 处理的逻辑与React的渲染时机不同步。我的意思是,这不像我们拿到数据后显示数据。我们触发一个与呈现并行的过程。另外,我们也不希望在每次呈现 FriendStatus 时都触发 onRender 。有一个API可以处理这种情况——我们可以传递一个变量数组作为 useEffect 的第二个参数:

useEffect(function componentDidMount() {
  ChatAPI.subscribeToFriendStatus(
    props.friend.id,
    status => setIsOnline(status.isOnline)
  );
}, [numberOfFriends]);

当第一次看到 useState 的时候,这是我脑海中浮现的第一个问题“他们是如何做到的?”。当我看到Angular 2的依赖注入时,我也有同样的感觉。尽管Dan解释说,这个功能背后并没有真正的魔法,但它给人的感觉很神奇。

为了使 hooks 正常工作,我们必须遵循某些规则。例如,我们必须在函数顶部定义钩子,并避免将它们放在 if 语句或 for 循环中。

写到最后

正如我在文章开头说的,React中的 hooks 是实验性的,它们仍然是一个提案。你不应该用 hooks 重写你的应用,因为它们的API可能会改变。我认为这些 hooks 是朝着正确方向迈出的一步。然而,它们需要某种思维方式的转变才能被采用。这是因为它们不仅是一种模式,而且是一种新的范式,能够显著改变我们构建应用的方式。

hooks是一种新的组合方式和新的逻辑共享方式。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

乔纳森传

乔纳森传

利恩德·卡尼 / 汪琪 岳卉 王文雅 / 中信出版社 / 2014-1-1 / 49

抛开苹果公司,单就设计行业来讲,乔纳森也是一个特殊的人物。他推动了设计行业的大变革:不再为产品增加看起来炫得多的配件,而是要去掉多余的东西。 ——陈向东 终于有一本书能够如此地接地气:它不再关注那位神一样的乔布斯,而是关注那位站在神的背后,同样具有神一样光环的乔纳森。 ——孙陶然 乔纳森•艾夫把他自己对科学、人文、艺术,乃至整个世界的感知尽数渗透进苹果的设计和审美之中,他是......一起来看看 《乔纳森传》 这本书的介绍吧!

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具