Hooks

Hooks出来快一年了,之前一直持观望态度,了解但没深入过。
最近项目上升级了React Native,借着这个机会重构了一个比较大的页面,比较深入的使用了Hooks,遂有此篇。

本文主观因素较多,如果想系统的学习一下,还是建议去看官网英文文档:https://reactjs.org/docs/hooks-intro.html

第一印象

Hooks是React Team在2018年提出的一种在函数形式的组件中使用state的方法,在此之前我们要使用state的话只能创建一个Class形式的Component。(以下例子大部分来自官网文章)

class Counter extends React.Component {
  constructor(props){
    super(props);
    this.state = {
      count: 0  
    };
  }

  handleClick = () => {
    this.setState({
      count: count+1
    });  
  };

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

但现在,我们可以用很短几行代码用Hooks实现一遍:

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

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

可以说是十分Cooooooooooooool了

What Hooks & How Hooks

本文主要介绍两种Hooks:

useState

顾名思义,作用是使用State。
入参是state的初始状态,返回一个数组,第一个元素是state的当前值,第二个元素是state的set方法,你可以用析取把他们取出来顺便起个名字。
当然React并没有对useState做什么黑魔法,所以数组对象和Object对象还是会存在一些关于immutable的问题。

useEffect

看到Effect这个词,了解一点函数式的同学可能会神经一紧。可以参考一下我去年写的文章:https://www.jianshu.com/p/398dd160b2b0
狭义的讲,在组件(可以是一个js意义上的函数,也可以是一个业务意义上的基本单元)运行过程中,修改了外部变量导致组件下一次使用时产生不一样的结果,广义的讲任何涉及组件外部环境的操作,都是副作用。
副作用包括但不限于:
修改一个外部变量;
Call一个API;
打了一个console.log;

所以useEffect也就是使用副作用。

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

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

  useEffect(() => {
    // Update the document title using the browser API
    document.title = `You clicked ${count} times`;
  });

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

useEffect稍微复杂一点,它实际上接收两个参数。

useEffect(()=>{}, [])

第一个参数是一个函数,是这个副作用的具体实现。如果涉及到资源的回收的话,这个函数的返回值应当是一个函数,用来包装你的资源回收逻辑。比如你在useEffect里subscribe了某个stream,那么应当在返回函数里面对其进行销毁。
例如

useEffect(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }

    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });

第二个参数是一个数组,是这个副作用会被哪些参数trigger。如果不传的话则是会被组件任何props和state的变化trigger。如果传空数组的话则说明不会被任何参数trigger,那么这个effect只会运行初始的一次。

const AdvancedCounter = ({name}) => {
  const [count, setCount] = useState(0);

  useEffect(() => {
    // Update the document title using the browser API
    document.title = `You clicked ${count} times, ${name}`;
  }, [count]);

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

在上面这个例子里,effect只会被count这个state触发。
同时我们应该注意到一点,effect的每次触发都会调一个新的函数。

Why Hooks

1. 用更函数式的方法来使用state

React是一个很函数式的lib,但要使用state这种基础功能的时候却要用class...

2. 有了一种新的方法来封装业务的基本单元

之前的选择很简单,如果是stateful的组件,就用class。如果没有state,就使用function。现在我们基本不用考虑了,用hooks能满足大部分需求。

3. 干掉了生命周期

我不知道有多少人和我一样痛恨React的生命周期方法,虽然某种意义上来讲生命周期也是一种Hooks。
因为生命周期的存在,我们不得不在构思一个组件的一开始就想好它在这个组件生命周期的每个阶段它应该长什么样。
举个例子,现在Counter这个组件有一个需求是要先去后台访问一个get_counter的API来得到现在这个网站的Counter已经被多少个人点过了。
如果使用class形式的component,第一想法是在componentDidMount里面去subscribe这个API,结果放在state里,在componentWillUnmount的里面unsubscribe释放资源。
到目前为止一切还算Nice,简单,可控。
我们的第二个需求来了,我们要在counter能被某一个数整除的时候给现在这个用户弹一个窗口给他一个大大的Surprise,咋整?那在componentDidUpdate里面写吧。
现在还行,组件已经有一点复杂了。
第三个需求:counter能被另一个数字取余等于3的时候给用户弹个窗口再给个Surprise,如果同时满足2和3那让用户的屏幕炸掉。
能做不?
我们想了一下,在componentDidUpdate里面要setState让用户屏幕炸掉,同时不能死循环,要立一个flag,然后还要执行两个不一样的判断。卧槽,复杂度爆表啊。
这个需求砍了吧,没意思。

如果使用Hooks的话,实际上需要4个Hooks。但因为每个Hooks不直接相关,大大简化了复杂度。

4. 抛弃了HOC这种复杂的组件组合方式

想象一下有一个万能的API,我们有些组件需要使用这个API的返回值,有些组件不需要,怎么去share这个stateful的logic?
之前的抽象方法是把这个API封装在一个Component里面,如果组件A需要使用这个API,那么:

const withAPI = (Component) => 
  class extends React.Component {
    ......
  }

const A = () => {return <div />};

const AWithAPI = withAPI(A);

如果只有一个可能还好,我们只需要封装这么一层。
如果逻辑很复杂呢?
那么你的代码很可能会变成这样:withAll = (Component) => withA(withB(withC(withD(withWTF(Component)))))

如果使用Hooks,ABCDWTF都可以封装在Hooks里面,某个Component需要使用对应的特性,只需要在function里面call一下就行了。语法以外,这样的Hooks更像一个装饰器。

Thinking in Hooks

使用了Hooks,就不要再被Class的既有经验影响。
总的来说,Hooks是对组合(Composition)这种方法的比较好的实现。
我们在用的时候,也应该摒弃HOC时代的一些旧有想法,不用再关注每层HOC之间的关系。
而应该把目光放在每个Hooks本身承担的业务单元以及该怎么组合这些业务单元来构建一个stateful的component。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,761评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,953评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,998评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,248评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,130评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,145评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,550评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,236评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,510评论 1 291
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,601评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,376评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,247评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,613评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,911评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,191评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,532评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,739评论 2 335

推荐阅读更多精彩内容