Why React Hooks

栏目: 服务器 · 发布时间: 4年前

内容简介:未完,待续。原文地址:上面是两个最简单的 function component 和 class component 的对比,首先从行数上来看,3 << 7。再看 babel 编译成 es2015 后的代码:

未完,待续。原文地址: github.com/rccoder/blog/issues/33

一、前言

1.1 为何要优先使用 SFC(Stateless Function Component)

Stateless Function Component:

const App = (props) => (
  <div>Hello, {props.name}</div>
)

Class Component:

class App extends React.Component {
  render() {
    return (
      <div>Hello, {this.props.name}</div>
    )
  }
}

上面是两个最简单的 function component 和 class component 的对比,首先从行数上来看,3 << 7。

再看 babel 编译成 es2015 后的代码:

Function Component:

"use strict";

var App = function App(props) {
  return React.createElement("div", null, "Hello, ", props.name);
};

Class Component:

去除了一堆 babel helper 函数

"use strict";

var App =
/*#__PURE__*/
function (_React$Component) {
  _inherits(App, _React$Component);

  function App() {
    _classCallCheck(this, App);

    return _possibleConstructorReturn(this, _getPrototypeOf(App).apply(this, arguments));
  }

  _createClass(App, [{
    key: "render",
    value: function render() {
      return React.createElement("div", null, "Hello, ", this.props.name);
    }
  }]);

  return App;
}(React.Component);

Function Component 仅仅是一个普通的 JS 函数,Class Component 因为 ES2015 不支持 class 的原因,会编译出很多和 class 相关的代码。

同时因为 Function Component 的特殊性,React 底层或许可以做 更多的性能优化

总的来说,以下点:

  • 更容易阅读和单测
  • 写更少的代码,编译出更加精简的代码
  • React Team 可正对这种组件做更加的性能优化

1.2 恼人的 bind(this)

在 React Class Component 中,我们一定写过很多这样的代码

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
    	name: 'rccoder',
    	age: 22
    },
    this.updateName = this.updateName.bind(this);
    this.updateAge = this.updateAge.bind(this);
  }
  
  render() {
    <div onClick={this.updateName}	
</div>
  }
}

当然这个错不在 React,而在于 JavaScript 的 this 指向问题,简单看这样的代码:

class Animate {
  constructor(name) {
    this.name = name;
  }
  getName() {
    console.log(this);
    console.log(this.name)
  }
}

const T = new Animate('cat');
T.getName();  // `this` is Animate Instance called Cat

var P = T.getName;
P(); // `this` is undefined

这个例子和上面的 React 如出一辙,在没有 bind 的情况下这样写会 导致了 this 是 global this,即 undefined。

解决它比较好的办法就是在 contructor 里面 bind this。

在新版本的 ES 中,有 Public Class Fields Syntax 可以解决这个问题,即:

class Animate {
  constructor(name) {
    this.name = name;
  }
  getName = () => {
    console.log(this);
    console.log(this.name)
  }
}

const T = new Animate('cat');
T.getName();  // `this` is Animate Instance called Cat

var P = T.getName;
P(); // `this` is Animate Instance called Cat

箭头函数不会创建自己的 this,只会依照词法从自己的作用域链的上一层继承 this,从而会让这里的 this 指向恰好和我们要的一致。

即使 public class fileds syntax 借助 arrow function 可以勉强解决这种问题,但 this 指向的问题依旧让人 “恐慌”。

1.2 被废弃的几个生命周围

React 有非常多的生命周期,在 React 的版本更新中,有新的生命周期进来, 也有一些生命周期官方已经渐渐开始认为是 UNSAFE 。目前被标识为 UNSAFE 的有:

  • componentWillMount
  • componentWillRecieveProps
  • componentWillUpdate

新引入了

  • getDerivedStateFromProps
  • getSnapshotBeforeUpdate

getDerivedStateFromPropsgetSnapshotBeforeUpdate 均是返回一个处理后的对象给 componentDidUpdate ,所有需要操作的逻辑都放在 componentDidUpdate 里面。

原则上, getDerivedStateFromProps + componentDidUpdate 可以替代 componentWillReceiveProps 的所有正常功能, getSnapshotBeforeUpdate + componentDidUpdate 可以替代 componentDidMount 的所有功能。

具体的 原因迁移指南 可以参考 React 的官方博客: Update on Async Rendering ,有比较详实的手把手指南。

最后,你应该依旧是同样的感觉,Class Component 有如此多的生命周期,显得是如此的复杂。

说了上面一堆看似和题目无关的话题,其实就是为了让你觉得 “Function Component 大法好”,然后再开心的看下文。

二、什么是 React Hooks

终于来到了和 Hooks 相关的部分,首先我们看下 什么是 Hooks

2.1 什么是 Hooks

首先来看下我们熟知的 WebHook:

Webhooks allow you to build or set up GitHub Apps which subscribe to certain events on GitHub.com. When one of those events is triggered, we'll send a HTTP POST payload to the webhook's configured URL. Webhooks can be used to update an external issue tracker, trigger CI builds, update a backup mirror, or even deploy to your production server. You're only limited by your imagination.

—— GitHub WebHook 介绍

核心是:When one of those events is triggered, we'll send a HTTP POST payload to the webhook's configured URL

2.2 什么是 React Hooks

那 React Hooks 又是什么呢?

Hooks are a new addition in React 16.8. They let you use state and other React features without writing a class.

看上去和 WebHook 区别不小,实际上也不小,怎么解释呢?

React Hook 是在 Function Component 的 state 和 生命周期 上开了 Hook(钩子),React Hooks 提供了一些内置的 Hook 以便在合适的时机去使用它,同时组合内置的 Hook + 自己的业务逻辑 就可以生成新的 Custom Hook,同样也可以挂载在 React Function Component 上。

三、Hooks 之前的一些问题

3.1 不同组件间逻辑复用问题

很多时候,视图表现不同的组件都期望拥有一部分相同的逻辑,比如:强制登录、注入一些值等。这个时候我们经常会使用 HOC、renderProps 来包装这层逻辑,总的来说都会加入一层 wrapper,使组件的层级发生了变化,随着业务逻辑复杂度的增加,都会产生 wrapper 地狱的问题。

3.2 复杂组件阅读困难问题

随着业务逻辑复杂度的增加,我们的组件经常会在一个生命周期中干多见事,比如:在 componentDidMount 中请求数据、发送埋点等。总之就是在一个生命周期中会写入多个完全不相关的代码,进而造成各种成本的隐形增加。

假如因为这些问题再把组件继续抽象,不仅工作量比较繁杂,同时也会遇到 wrapper 地狱和调试阅读更加困难的问题。

3.3 class component 遇到的一些问题

从人的角度上讲,class component 需要关心 this 指向等,大多经常在使用 function component 还是 class component 上感到困惑;从机器的角度上讲,class component 编译体积过大,热重载不稳定

四、Hooks 有哪些功能

上述提到的三个问题,Hooks 某种意义上都做了自己的解答,那是如何解答的呢?

目前 Hooks 有: State Hook、Effect Hook、Context Hook、以及 Custom Hook。

4.1 State Hook

import React, { useState } from 'react';

function Example() {
  // Declare a new state variable, which we'll call "count"
  const [count, setCount] = useState(0);

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

useState 是 State Hook 的 API。入参是 initState,返回一个 turple,第一值是 state,第二个值是改变 state 的函数。

4.2 Effect Hook

import React, { useState, useEffect } from 'react';

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

  // Similar to componentDidMount and componentDidUpdate:
  useEffect(() => {
    // Update the document title using the browser API
    document.title = `You clicked ${count} times`;
    return () => {
      ... // Similar to componentWillUnMount
    }

  });

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

useEffect 相当于 class Component 中 componentDidMountcomponentDidUpdatecomponentWillUnmount 三个生命周期的大综合,在组件挂载、更新、卸载的时候都会执行 effect 里面的函数。

在一个 Function Component 里,和 useState 一样可以可以使用多次 useEffect,这样在组织业务逻辑的时候,就可以按照业务逻辑去划分代码片段了(而不是 Class Component 中只能按照生命周期去划分代码片段)。

4.3 Custom Hook

import React, { useState, useEffect } from 'react';

function useFriendStatus(friendID) {
  const [isOnline, setIsOnline] = useState(null);

  function handleStatusChange(status) {
    setIsOnline(status.isOnline);
  }

  useEffect(() => {
    ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
    };
  });

  return isOnline;
}

function FriendListItem(props) {
  const isOnline = useFriendStatus(props.friend.id);

  return (
    <li style={{ color: isOnline ? 'green' : 'black' }}>
      {props.friend.name}
    </li>
  );
}

useFriendStatus 就是一个典型的 Custom Hook,他利用 useState 和 useEffect 封装了 订阅朋友列表,设置朋友状态,组件卸载时取消订阅 的系列操作,最后返回一个表示是否在线的 state;在使用的时候,就可以像内置 Hook 一样使用,享用封装的系列逻辑。

在 Hooks 内部,即使在一个 Function Component 中每个 Hooks 调用都有自己的隔离空间,能保证不同的调用之间互不干扰。

useFriendStatususe 开头是 React Hooks 的约定,这样的话方便标识他是一个 Hook,同时 eslint 插件也会去识别这种写法,以防产生不必要的麻烦。

4.4 Context Hook

function Example() {
  const locale = useContext(LocaleContext);
  const theme = useContext(ThemeContext);
  // ...
}

4.5 Reduce Hook

function Todos() {
  const [todos, dispatch] = useReducer(todosReducer);
  // ...

五、例子对比

该例子是 Dan 在 React Conf 上的例子,算是非常有代表性的了:

六、引入的问题

6.3 奇怪的 useEffect

6.2 底层实现导致逻辑上的问题

React Hook 在内部实现上是使用 xxx,因为使用 React Hook 有两个限制条件

  • 只能在顶层调用 Hooks,不能在循环、判断条件、嵌套的函数里面调用 Hooks
  • 只允许 Function Hooks 和 Custom Hooks 调用 React Hook,普通函数不允许调用 Hooks

React Team 为此增加了 eslint 插件: eslint-plugin-react-hooks ,算是变通的去解决问题吧。

七、常见疑问

为什么 useState 返回的是个 tuple

reactjs/rfcs#68 (comment)

性能问题

能标识所有的生命周期么?

八、参考资料


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

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

数据库系统实现

数据库系统实现

加西亚-莫利纳(Hector Garcia-Molina)、Jeffrey D.Ullman、Jennifer Widom / 杨冬青、吴愈青、包小源 / 机械工业出版社 / 2010-5 / 59.00元

《数据库系统实现(第2版)》是斯坦福大学计算机科学专业数据库系列课程第二门课的教科书。书中对数据库系统实现原理进行了深入阐述,并具体讨论了数据库管理系统的三个主要成分——存储管理器、查询处理器和事务管理器的实现技术。此外,第2版充分反映了数据管理技术的新进展,对内容进行了扩充,除了在第1版中原有的“信息集成”一章(第10章)中加入了新的内容外,还增加了两个全新的章:“数据挖掘”(第11章)和“数据......一起来看看 《数据库系统实现》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

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

RGB CMYK 互转工具