React 16新特性全解 (下)--- 一文读懂Hooks

前言

这篇文章主要讲Hooks,若是你想了解React 16的其余新特性,请移步 React 16新特性全解 (上)React 16新特性全解 (中)html

v16.8 Hooks

Hooks是什么?react

咱们知道,functional component在使用的时候有一些限制,好比须要生命周期、state的时候就不能用functional component。 而有了Hooks,你就能够在funtional component里,使用class component的功能:props,state,context,refs,和生命周期函数等等。npm

虽然Hooks已经有要取代正宫class的趋势了,可是react目前没有计划抛弃class,因此不要慌,你仍是能够跟往常同样使用class。bash

在真正介绍Hook以前,仍是同样先来了解为何要引入Hooks?其实不仅仅是为了给functional component赋于class component的功能。app

还有下面的问题:函数

1.组件之间很难复用逻辑工具

以前若是咱们须要复用逻辑,经常使用的两种方式是render propshigher-order components。可是这两种方式都须要你重构代码,因此比较麻烦。学习

最重要的是,用这两种方式的话,在React Devtools里,会看到不少的嵌套组件。fetch

在这个图能够看到Header外层套着不少嵌套组件。ui

2.复杂组件很难理解

在以前的class component里,咱们的生命周期函数里一般放着不相关的代码,而相关的代码确要放在不一样的生命周期函数里。这样说可能有点绕,咱们来看一个具体的例子。

class App extends React.component {
  componentDidMount() {
    window.addEventListener('scroll', () => {console.log('a')})
    this.fetchData();
  }
  componentDidUpdate() {
    this.fetchData();
  }
  componentWillUnmount() {
    window.removeEventListener('scroll', () => {console.log('a')})
  }
  render() {
    return <div>ddd</div>
  }
}
复制代码

这应该是咱们平时会常常写的代码。在componentDidMount里作事件绑定,获取数据。在componentWillUnMount里解绑事件。

这样作的问题是:componentDidMount装着的代码都是不相关的,而相关联的事件绑定以及事件解绑,分散在componentDidMount 跟 componentWillUnMount里。这样若是组件的逻辑越写越复杂以后,就会变得很难维护易出bug。

后面讲Effect Hook的时候,我会介绍这个问题用Hooks怎么解决。

3.class比较难学

React团队发现class是初学者学习React的大障碍。要学习class component,你必需要知道几点:

  1. this在JS是如何工做的(光是这个就够绕的)
  2. 记得绑定事件
  3. 了解state,props,state以及从上而下的数据流
  4. functional component跟class component的区别,如何使用它们

如何使用

理解了Hooks诞生的缘由,接着来看看要如何使用。

假设我须要实现一个功能,点击app时候,count数目加一。用class的形式实现就是 这样的:

Class:

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

  render() {
    return (
      <div>
        <p>You clicked {this.state.count} times</p>
        <button onClick={() => this.setState({ count: this.state.count + 1 })}>
          Click me
        </button>
      </div>
    );
  }
}
复制代码

Hooks:

若是须要用Hooks实现,变成

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> ); } 复制代码

演示地址

在这里demo中。useState就是Hook。咱们经过它来在function component里加入state数据。

二者对好比下:

1. 定义state变量

Class:

class Example extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }
复制代码

Hooks:

import React, { useState } from 'react';

function Example() {
  // 经过useState这个Hooks定义state变量:count。而且经过useState给count赋初始值0,只在初始化时候使用一次
  const [count, setCount] = useState(0);
复制代码

在function component里,咱们是没有this的。因此没办法向Class那样用this来建立state,这时候Hooks闪亮登场。 经过useState这个hooks咱们能够定义count这个state变量。 由Hooks定义的state变量不必定要是object,能够是string、number。传入的内容至关于给变量赋初始值。

function ExampleWithManyStates() {
  // Declare multiple state variables!
  const [age, setAge] = useState(42);
  const [fruit, setFruit] = useState('banana');
  const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
  // ...
}

复制代码

2. 渲染state

Class:

<p>You clicked {this.state.count} times</p>
复制代码

Hooks:

<p>You clicked {count} times</p>
复制代码

能够不须要用this,直接使用count

3.更新state

Class:

<button onClick={() => this.setState({ count: this.state.count + 1 })}>
    Click me
  </button>
复制代码

Hooks:

<button onClick={() => setCount(count + 1)}>
    Click me
  </button>
复制代码

咱们能够看到 const [count, setCount] = useState(0);useState返回两个参数,一个是当前state的值,还有一个实际上是一个函数,用来改变state的值,就是setCount。

相似setState,可是不一样的是,它不会将旧的state跟新的state合并在一块儿,而是覆盖式的重写state的值。

说完了functional component里面如何使用state以后,咱们再来看如何用Effect Hook来取代生命周期。

通常咱们都会在生命周期componentDidMount, componentDidUpdate与 componentWillUnmount中作一些反作用的操做,例如:获取数据,订阅,手动改变DOM。而在hooks里,这些生命周期函数都被统一成一个方法 useEffect。

下面咱们来举个例子:

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 (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}
复制代码

在这个例子中,咱们在useEffect 里完成了反作用的操做。demo演示

Effects Hooks 就在functional component里, 因此它能够直接访问props跟state。 React在每次render以后都会调用effect,包括第一次render。

可是这里还遗留两个问题

一、咱们在开篇说到,class component有个问题就是生命周期函数里的代码都是不相关的,而相关的代码确要被打散在不一样的生命周期函数里。这个问题用Hooks的话就能够解决。好比绑定、解绑事件,在使用class的时候,在componentDidMount里 监听了一个事件,以后须要在componentWillMount里给它解绑。

用Hook只须要在useEffect一个函数就能够作到。它能够经过返回一个函数来专门作清除的工做,代码以下:

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

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

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

  useEffect(() => {
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
   
    // 在这里返回一个函数来作这件事
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });

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

在这个case中,unsubscribeFromFriendStatus不只仅会在组件unmount的时候 调用,同时在从新渲染的时候也会调用。

若是你只想useEffect只在mount与unmount时候调用,须要这样传一个[] 做为第二个参数。

useEffect(() => {
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);

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

复制代码

二、有时候咱们并不想每次state的改变,都去调用useEffect。 在class里,会这样作

componentDidUpdate(prevProps, prevState) {
  if (prevState.count !== this.state.count) {
    document.title = `You clicked ${this.state.count} times`;
  }
}
复制代码

在Hooks,能够经过给useEffect传入第二个参数,即它关注的state变量,来作到这件事。

例如:当count改变时候,才去调用useEffect。

useEffect(() => {
  document.title = `You clicked ${count} times`;
}, [count]); // Only re-run the effect if count changes
复制代码

注意事项:

  1. Hooks只能够在顶层使用。也就是说它不能写在循环体,条件渲染,或者嵌套function里
  2. 只能够在React的function组件里使用Hooks。

说了那么多,总结如下,其实Hooks就是帮助咱们在function component里直接使用原来class component才有的特性。

以上,咱们只接触到了两种hooks,还有更多好比useContext, useReducer, useCallback,感兴趣的同窗能够本身看下~

最后是使用Hooks的一些建议:

一些建议

  1. 是否须要用hoooks重构之前的代码?

No,react团队不推荐用hooks从新写一遍。 推荐作法是新的组件能够直接使用,而后须要改老组件代码的时候在顺便改就好了。s

  1. 同时建议使用

Hooks可让你使用在不须要写class的状况,只用

  1. 支持Hooks的工具

React DevTools对hooks已经支持。同时强烈推荐安装hooks的eslint校验库 eslint-plugin-react-hooks

Reac团队下一步计划

由于如今React Hooks还不能彻底cover全部class的功能,虽然他们已经很相近了。目前为止Hooks不支持两个不常使用的API getSnapshotBeforeUpdate 跟 componentDidCatch,可是React团队正在努力,将来会支持的。

相关文章
相关标签/搜索