咱们都知道,在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的方式建立该组件显得大材小用,所以在这种状况下,咱们更推荐使用函数组件来建立。编程
那么函数组件有哪些优势呢?数组
既然函数组件有那么多的优势,那么为何咱们还须要类组件呢?缓存
而以上功能,在函数式组件中是不存在的,所以咱们必须使用类组件。知道React v16.8,React给咱们带来了hooks,hooks就是用来提供类组件中有而函数组件中没有的功能。bash
React提供的hooks有不少,可是如下几种基本涵盖了咱们业务中百分之八九十的场景:react-router
咱们从命名中能够看出,hooks采用约定的方式以use开头,这让我想起了HOC,HOC每每都是以with开头,就像withRouter同样,咱们的组件赋予更强的能力。app
这就是hooks的由来,这就是hooks,那么hooks怎么用呢?函数式编程
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能够说是componentDidmount和compoenntDidUpdate的结合体,咱们能够简单列下useEffect如何达到这两种效果。
useEffect(() => {
// mount时会调用
},[])
复制代码
其中第二个参数能够传一个数组,若是数组为空,则只在componentDidMount的时候执行。若是传入一个state,当该state变化致使页面从新渲染时,useEffect也会执行,达到componentDidUpdate的效果。
useEffect(() => {
// mount或者update都会调用
})
复制代码
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时执行的效果。
useEffect(() => {
// mount时会调用
return () => {
// unmount时会调用
}
},[])
复制代码
useEffect的第一个参数能够return一个方法,这个方法就用于在componentDidUnMount时执行。
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,而后在各组件中灵活复用,提升了代码的利用率,减小了冗余代码。
<Wrapper render={({clickEvent}) => (
<button onClick={clickEvent} />
)}/>
复制代码
另一种方式是HOC,例如withRouter将react-router 的 history、location、match 三个对象传入prop。而hooks的custome hooks能够为咱们更好的复用组件。
React v16.8带来的hooks必定程度上给咱们提供了巨大的便利,提供了咱们的工做效率,减小了冗余的代码。hooks也是一种趋势,业界也在一直拥抱hooks,所以若是你是react的忠实用户,勇敢使用hooks吧!