React Hooks & Function/Class component

705 阅读3分钟

Function Component 和 Class Component 区别

语法

  • functional component语法更简单,只需要传入一个props参数,返回一个react片段。
  • class component 要求先继承React.Component然后增加一个render方法,在render里面返回react片段

有无state状态

  • 因为function component 只是一个普通的函数所以不可以在其中用this.state , setState(),这也是它被叫做无状态组件的原因。
  • 一个组件需要用到状态的时候要用到class component。

声明周期

  • function component 不具有生命周期,因为所有的生命周期钩子函数均来自于React.Component。
  • 所以当一个组件需要生命周期钩子的时候我们也需要class component

各自优势

  • Function Component因为没有状态,可以只负责表现层的逻辑。不用考虑因为复杂的逻辑去改变状态,这样有利于代码复用。

  • Class Component 。当需要实现一些容器组件的时候,需要改变内部状态;当需要用到生命周期钩子函数实现一些功能的时候;在组件内部用shouldComponentUpdate 方法来去判断,减少组件渲染次数,可以提升性能。

但是有了React Hooks之后,Function Component配合上Hooks就很强大了。

为什么不用class component,要用Hooks?

想要复用一个有状态的class组件过于麻烦

class组件本身包含了状态,难以复用。

有了hooks,不需要写class,所有的组件都是function。Hooks允许您在不更改组件层次结构的情况下复用有状态的逻辑。这样可以轻松的在许多组件之间共享Hooks。

代替生命周期

生命周期钩子函数里的逻辑混。比如我们需要在componentDidMount中发起ajax请求获取数据,绑定一些事件监听等,有时候我们还需要在componentDidUpdate做一遍同样的事情,这样代码很不直观。使用Hooks用一条useEffect可以直接做到componentDidMount+componentDidUpdate,而且很直观。

class内的this指向让人困惑  

使用class创建组件时,要时刻注意this的指向。一旦忘了绑定this,会有各种bug。使用Hooks就不需要面对this,比如 const [count, setCount] = useState(0); 里面的count只是一个变量,可以直接用了,不需要 this.state.count。

使用React Hooks模拟生命周期

constructor

React Hooks基于函数组件,不需要构造函数。可以通过调用useState初始化state

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

componentDidMount

把useEffect第二个参数设为空数组 [],这样的话回调函数只会在首次渲染之后执行一次,类似于componentDidMount

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  }, []); 

可以给useEffect传第二个参数,另这个数组存在并有值,只有当渲染后数组中的任何值发生更改,才会执行回调。

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  }, [count]); //只有当count的值发生变化时,才执行副作用函数

componentDidUpdate

如果useEffect的第二个参数不存在首次渲染和之后的每次渲染都会调用回调函数,相当componentDidMount+componentDidUpdate

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

componentWillUnmount

在useEffect的回调函数中返回一个函数,我们可以在这里面清除定时器或事件监听器,或者解除订阅。

下面这种情况,这个函数会在组件卸载前被调用,相当于componentWillUnmount

  useEffect(() => {
    return () => {
      console.log('Will Unmount');
    };
  }, []);

下面这种情况是一个发布订阅模式,让我们传给useEffect的副作用函数返回一个新的函数,这个新的函数将会在组件下一次重新渲染之后执行

import { 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);
    // 一定注意下这个顺序:告诉react在下次重新渲染组件之后,同时是下次调用ChatAPI.subscribeToFriendStatus之前执行cleanup
    return function cleanup() {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });

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

代码执行顺序是这样的

1.页面首次渲染
2.替friend.id=1的朋友注册

3.突然friend.id变成了2
4.页面重新渲染
5.清除friend.id=1的绑定
6.替friend.id=2的朋友注册
...

第一次渲染,注册第一个

每次重新渲染,都会清掉之前的,注册最新的

组件卸载前会把最后一个清掉

segmentfault.com/a/119000001…

zh-hans.reactjs.org/docs/hooks-…