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是一种新的组合方式和新的逻辑共享方式。


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

查看所有标签

猜你喜欢:

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

Text Processing in Python

Text Processing in Python

David Mertz / Addison-Wesley Professional / 2003-6-12 / USD 54.99

Text Processing in Python describes techniques for manipulation of text using the Python programming language. At the broadest level, text processing is simply taking textual information and doing som......一起来看看 《Text Processing in Python》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

URL 编码/解码
URL 编码/解码

URL 编码/解码