React hooks的使用小结

什么是hooks

咱们都知道,在React中一切都是组件,而组件的建立方式有多种。若是你是久经战场的React开发者,那你确定知道createclass建立组件的方式,可是这种方式已通过时了。目前经常使用的两种建立组件的方式是使用es6的class来建立组件,咱们称为类组件,以及使用普通的函数来建立组件,咱们称之为函数组件。在React V16.8以前,类组件和函数组件有着不一样的功能,所以也适用于不一样的适用场景。可是由于功能比较接近而又有着差别,所以让不少人难以抉择使用哪一种方式来建立组件。 例如,咱们在建立纯展现组件的时候,咱们使用类组件建立方式以下:react

class Hello extends React.Component {
  render() {
    return <div>Hello</div>;
  }
}
复制代码

使用函数组件建立方式以下:es6

const Hello = () => {
  return <div>Hello</div>;
};
复制代码

显而易见,使用class的方式建立该组件显得大材小用,所以在这种状况下,咱们更推荐使用函数组件来建立。编程

那么函数组件有哪些优势呢?数组

  • 简单易懂。从上面能够看出来,函数式组件相比类组件简单易懂,并且编译出来的代码相比类组件更简单,由于类组件在在编译过程当中须要将es6的class转为es5,同时须要继承React.Component。而函数组件只需将箭头函数转为普通的函数。
  • 更符合React UI=f(state)的哲学。函数组件更符合UI=f(state)的哲学,由于React自己就是一个画UI的库,f就是咱们编写的组件,state就是咱们的数据,咱们只关心数据传到咱们的组件中获得咱们想要的界面。而类组件的方式并不符合这种哲学。
  • 更符合函数式编程的思想。函数式编程的思想是近年逐渐火起来的一种编程范式,React也一种在推崇函数式编程的思想,但不管如何都没法彻底作到。函数式编程中纯函数、不可变数据、函数组合、声明式的特性也给代码减小了bug,提供了更好的代码稳定性。而函数组件更符合这种思想。

既然函数组件有那么多的优势,那么为何咱们还须要类组件呢?缓存

  • 组件须要state状态
  • 组件须要例如shouldComponentUpdate等生命周期
  • 组件须要在生命周期中进行反作用

而以上功能,在函数式组件中是不存在的,所以咱们必须使用类组件。知道React v16.8,React给咱们带来了hooks,hooks就是用来提供类组件中有而函数组件中没有的功能。bash

React提供的hooks有不少,可是如下几种基本涵盖了咱们业务中百分之八九十的场景:react-router

  • useState
  • useEffect
  • custome hooks

咱们从命名中能够看出,hooks采用约定的方式以use开头,这让我想起了HOC,HOC每每都是以with开头,就像withRouter同样,咱们的组件赋予更强的能力。app

这就是hooks的由来,这就是hooks,那么hooks怎么用呢?函数式编程

hooks的简单使用

useState的使用

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

  const add = () => { setCount(count + 1); }; 

  return ( 
    <React.Fragment> 
       <h1>{count}</h1> 
       <button onClick={add}>add</button> 
    </React.Fragment> 
  ); 
};
复制代码

上面代码中,让咱们感到很陌生的就是第二行代码:函数

const [count, setCount] = React.useState(0); 
复制代码

那么咱们来简单分析一下useState。

在使用useState的时候,咱们须要先初始化,即给useState传入一个初始值0,useState会给咱们返回一个元组。元组的存在就是让咱们方便解构,元组的第一个数据是一个数值,即咱们定义的state值,第二个数据是修改该state值的方法。这两个值一一对应,且与外界没有任何关系。

这样咱们在add方法中执行修改state的方法时,即可设置state的数据。

这就是useState的简单使用。

在咱们的业务场景中,咱们每每须要定义多个state,那么咱们能够在函数的开头依次定义。

const Counter = () => { 
  const [count, setCount] = React.useState(0);
  const [count1, setCount1] = React.useState(10); 

  const add = () => { setCount(count + 1); };
  const minus = () => { setCount(count1 - 1); };

  return ( 
    <React.Fragment> 
       <h1>{count}</h1> 
       <button onClick={add}>add</button> 
       <h1>{count1}</h1> 
       <button onClick={minus}>minus</button> 
    </React.Fragment> 
  ); 
};
复制代码

写到这,确定有人会问,一个方法怎么保存状态?一个函数组件怎么保存状态?那么咱们来分析下useState的原理。 咱们都知道,每个组件都对应一个Fiber对象,每一个Fiber对象中都会有一个memorizeState的属性来存储组件内的状态。例如一个类组件中的全部状态都存储在Fiber对象的memorizeState中。而对于一个函数组件而言,当第一调用useState的时候,React会给这第一个state生成一个hooks对象,这个hooks对象就指向了Fiber对象的memorizeState属性,所以Fiber中的memorizeState属性也能够用来存储函数组件的状态。那么咱们来看看hooks对象:

{ 
  baseState,
  next,
  baseUpdate,
  queue,
  memoizedState 
}
复制代码

咱们能够看到,hooks对象中也包含一个memorizedState属性,这个属性就是用来存储当前的state的值。同时包含一个next属性,这个next属性就是用来指向下一次执行useState时生成的hooks对象,以此类推,按照函数组件第一次执行时useState的初始化顺序生成一个hooks对象的调用链,之后按照这个顺序依次取值。说到这里,useState中的调用顺序相当重要,加入咱们的代码是这样的

const Counter = () => { 
   const [count, setCount] = React.useState(0);
 
   if (count  === 0) { 
     const [count1, setCount1] = React.useState(1); 
   } 

   const [count2, setCount2] = React.useState(2);
复制代码

当函数组件第一次执行的时候,默认会初始化三个useState。顺序以下:

useState1 => memoizedState count = useState1.memoizedState

useState1.next => useState2 count2 = useState2.memoizedState

useState2.next => useState3 count3 = useState3.memoizedState

当咱们进行一系列操做以后,count的值由0变成其它数值了而且致使函数组件从新更新了,那么此时useState再次执行,执行顺序以下:

useState1 => memoizedState count === useState1.memoizedState

useState1.next => useState2 count2 === useState2.memoizedState

此时会把state1的值取出来赋值给count2,致使bug的出现。

所以,在使用useState时,相当重要的就是不管执行多少次,useState的执行顺序和执行数量都保持一致。

useEffect的使用

useEffect能够说是componentDidmount和compoenntDidUpdate的结合体,咱们能够简单列下useEffect如何达到这两种效果。

  1. componentDidMount
useEffect(() => {
  // mount时会调用
},[])

复制代码

其中第二个参数能够传一个数组,若是数组为空,则只在componentDidMount的时候执行。若是传入一个state,当该state变化致使页面从新渲染时,useEffect也会执行,达到componentDidUpdate的效果。

  1. componentDidUpdate 和 componentDidMount
useEffect(() => {
  // mount或者update都会调用
})
复制代码
  1. 模拟componentDidUpdate
const mounted = React.useRef(); 

React.useEffect(() => { 
  if (!mounted.current) { 
    mounted.current = true; 
  } else { 
    // update操做 
  } 
});
复制代码

咱们可使用React提供的另一种hooks:useRef来实现componentDidUpdate的效果。useRef会返回一个对象,该对象包含一个current属性,useRef主要用来存储整个生命周期中本身须要缓存的数据。当组件mount的时候,current是undefined,此时咱们给current赋值为true,这样在之后update的时候,current值永远为true,达到了只在componentDidUpdate时执行的效果。

  1. componentDidUnMount
useEffect(() => {
  // mount时会调用
  return () => {
    // unmount时会调用
  }
},[])
复制代码

useEffect的第一个参数能够return一个方法,这个方法就用于在componentDidUnMount时执行。

custome hooks的使用

const useAdd = initValue => {
  const [count, setCount] = React.useState(initValue);
  const addCount = () => {
   setCount(count + 1);
  };
  return [count, addCount];
};

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


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

复制代码

以上例子,咱们能够将操做加这个行为的逻辑和数据封装成一个useAdd hook,而后在各组件中灵活复用,提升了代码的利用率,减小了冗余代码。

React hooks给咱们带来了哪些好处?

  • 更好的代码复用性。咱们都知道,react的思想就是一个页面能够拆成n个组件,而后用但想数据流的方式将全部内容串联起来。若是咱们的react项目比较大,咱们会发现咱们的类组件每每很庞大也很难复用。在hooks以前,react官方推荐了两种方式来复用组件,一种是使用renderProps的方式,renderProps就是传递一个值为函数的prop来动态渲染组件,例如:
<Wrapper render={({clickEvent}) => ( 
    <button onClick={clickEvent} /> 
  )}/>
复制代码

另一种方式是HOC,例如withRouter将react-router 的 history、location、match 三个对象传入prop。而hooks的custome hooks能够为咱们更好的复用组件。

  • 彻底使用函数组件,减小生命周期的误用,更方便的管理状态
  • 以前咱们每每犹豫咱们当前组件是否使用函数组件,由于可能考虑后续该组件中会存在状态,那么在拥有hooks以后,彻底能够跳过这方面的考虑了。

总结

React v16.8带来的hooks必定程度上给咱们提供了巨大的便利,提供了咱们的工做效率,减小了冗余的代码。hooks也是一种趋势,业界也在一直拥抱hooks,所以若是你是react的忠实用户,勇敢使用hooks吧!

相关文章
相关标签/搜索