react hook 初体验

2020-01-15

定义:Hook 是一些可以让你在函数组件里“钩入” React state 及生命周期等特性的函数。Hook 不能在 class 组件中使用 —— 这使得你不使用 class 也能使用 React。

作用: React Hooks 要解决的是状态共享问题,但是只共享数据的处理逻辑,不会共享数据本身。

一、优势

  1. 组件间复用状态逻辑复杂
  2. 难于理解的class

二、State Hook基础

useState就是一个hook,它可以给组件添加内部state,它会返回一对值,当前状态和跟新它的函数。这个函数类似于class组件的this.setState。 下面这个例子是最简单的计数器:

import React, { useState } from 'react'
function Example() {
  const [count, setCount] = useState(0)
  return (
&nbsp;&nbsp;&nbsp;&nbsp;<div>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<button&nbsp;onClick={add}>点击+1
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<p>点击次数:{count}</p>
&nbsp;&nbsp;&nbsp;&nbsp;</button&nbsp;onClick={add}></div>
&nbsp;&nbsp;)
&nbsp;&nbsp;function&nbsp;add&nbsp;()&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;setCount&nbsp;(count&nbsp;+&nbsp;1)
&nbsp;&nbsp;}
}
export&nbsp;default&nbsp;Example

Image.png

三、Effect Hook

useEffect默认情况下在每次更新之后都会执行,使用useEffect可以方便的访问state或其他props。 一个完整的effect的格式如下:

useEffect(()&nbsp;=>&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;effect
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;()&nbsp;=>&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cleanup
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;};
&nbsp;&nbsp;&nbsp;&nbsp;},&nbsp;[input])

其中是effect是你需要执行的方法,return不是必须的,它返回的是一个清除机制。第二个参数[input]也不是必须的,指的是effect的依赖。如果第二个参数是[]表示的是仅在挂载和卸载的时候执行,不传表示在挂载和更新的时候执行。

四、Capture Value 特性

解释:每次 Render 的内容都会形成一个快照并保留下来,因此当状态变更而 Rerender 时,就形成了 N 个 Render 状态,而每个 Render 状态都拥有自己固定不变的 Props 与 State。什么是Cature Value如下面一个例子: `

function&nbsp;Example()&nbsp;{
&nbsp;&nbsp;const&nbsp;[count,&nbsp;setCount]&nbsp;=&nbsp;useState(0)
  return&nbsp;(<div><button&nbsp;onClick={add}>点击测试</button&nbsp;onClick={add}></div>)
&nbsp;&nbsp;function&nbsp;add()&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;setCount(5)
&nbsp;&nbsp;&nbsp;&nbsp;setTimeout(()&nbsp;=>&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;结果是0而不是5
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;console.log(count)
&nbsp;&nbsp;&nbsp;&nbsp;},&nbsp;1000)
&nbsp;&nbsp;}
}

打印的结果是0,而不是5,开始很让人费解。根据Capture Value特性,第一次挂载组件的时候,count就已经形成了一份快照,并保留下来了。setTimeout里面操作的count就是最开始的快照。 另一个例子说明了这个问题:

function&nbsp;Example()&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;[count,&nbsp;setCount]&nbsp;=&nbsp;useState(0);
&nbsp;&nbsp;&nbsp;&nbsp;useEffect(()&nbsp;=>&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setTimeout(()&nbsp;=>&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;console.log(`You&nbsp;clicked&nbsp;${count}&nbsp;times`);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;},&nbsp;3000);
&nbsp;&nbsp;&nbsp;&nbsp;});
&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;(
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<div>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<p>You&nbsp;clicked&nbsp;{count}&nbsp;times</p>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<button&nbsp;onClick={()&nbsp;=>&nbsp;setCount(count&nbsp;+&nbsp;1)}>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Click&nbsp;me
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</button&nbsp;onClick={()&nbsp;=></div>
&nbsp;&nbsp;&nbsp;&nbsp;);
}

Image [1].png

在快速点击按钮多下之后,上面结果说明每次更新都有一个快照保存了下来,每次打印出来的是对应的快照内容,而不是最后一次。接着你会想,我如何才能取到当前真正的值呢,而不是旧值。你需要使用useRef

function&nbsp;Example()&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;[count,&nbsp;setCount]&nbsp;=&nbsp;useState(0);
&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;latestCount&nbsp;=&nbsp;useRef(count);
&nbsp;&nbsp;&nbsp;&nbsp;useEffect(()&nbsp;=>&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;latestCount.current&nbsp;=&nbsp;count;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setTimeout(()&nbsp;=>&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;console.log(`You&nbsp;clicked&nbsp;${latestCount.current}&nbsp;times`);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;},&nbsp;3000);
&nbsp;&nbsp;&nbsp;&nbsp;});
&nbsp;&nbsp;&nbsp;&nbsp;return(
&nbsp;&nbsp;&nbsp;&nbsp;<div>Count:&nbsp;{count}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<button&nbsp;&nbsp;onClick={()=>{setCount(count+1)}}&nbsp;>点击我
&nbsp;&nbsp;&nbsp;&nbsp;</button&nbsp;&nbsp;onClick={()=></div>
&nbsp;&nbsp;&nbsp;&nbsp;)
}

Image [2].png

useRef返回了一个ref对象,它是可变的,其current属性指向的当前内容,而不是之前的快照。

五、如何设置一个定时器

现在有这样一个需求,进入页面后,需要一个数字不断的加一,离开页面后清除这个定时器,又由于依赖了count,那么很容易想到下面这种写法:

function&nbsp;Example()&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;[count,&nbsp;setCount]&nbsp;=&nbsp;useState(0)
&nbsp;&nbsp;&nbsp;&nbsp;useEffect(()&nbsp;=>&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;let&nbsp;inter&nbsp;=&nbsp;setInterval(()&nbsp;=>&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setCount(count&nbsp;+&nbsp;1)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;console.log('count-->',&nbsp;count)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;},&nbsp;1000)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;()&nbsp;=>&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;console.log('stopInterval-->',&nbsp;count)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;clearInterval(inter)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;};
&nbsp;&nbsp;&nbsp;&nbsp;},&nbsp;[count])
&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;(<div>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;当前的值是:{count}
&nbsp;&nbsp;&nbsp;&nbsp;</div>)
}

发现定时器能正常进行,并且页面可以显示数字:

Image [3].png

但是:为什么会不断的新建定时器后又销毁呢?这样实在是太不高效了。

Image [4].png

原来是因为useEffect依赖了count,当count改变后又反过来执行useEffect,其实setCount 还有函数回调方式,不用关心当前值。

function&nbsp;Example()&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;[count,&nbsp;setCount]&nbsp;=&nbsp;useState(0)
&nbsp;&nbsp;&nbsp;&nbsp;useEffect(()&nbsp;=>&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;let&nbsp;inter&nbsp;=&nbsp;setInterval(()&nbsp;=>&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setCount(c&nbsp;=>&nbsp;c&nbsp;+&nbsp;1)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;},&nbsp;1000)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;()&nbsp;=>&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;clearInterval(inter)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;};
&nbsp;&nbsp;&nbsp;&nbsp;},&nbsp;[])
&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;(<div>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;当前的值是:{count}
&nbsp;&nbsp;&nbsp;&nbsp;</div>)
}
export&nbsp;default&nbsp;Example

六、useReducer

在上一节中,我们发现更新和动作耦合在一起,尤其在一个useEffect依赖多个变量时候。useReduceruseState的替代方案,它接收(state, action) => newState方法,它返回一个数组,类似于useState hook,第一个是当前状态,第二个是dispatch方法。

&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;[state,&nbsp;dispatch]&nbsp;=&nbsp;useReducer(reducer,&nbsp;initialState,&nbsp;init)

利用useReducer可以重写最开始的加法器:

function&nbsp;Example()&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;[count,&nbsp;dispatch]&nbsp;=&nbsp;useReducer(reducer,&nbsp;0)
&nbsp;&nbsp;&nbsp;&nbsp;function&nbsp;reducer&nbsp;(state,&nbsp;action)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;action.type&nbsp;===&nbsp;'add'&nbsp;?&nbsp;state&nbsp;+&nbsp;1&nbsp;:state&nbsp;-&nbsp;1
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;(<div>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<button&nbsp;onClick={()&nbsp;=>&nbsp;{dispatch({type:&nbsp;'add'})}}>+1
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<button&nbsp;onClick={()&nbsp;=>&nbsp;{dispatch({type:&nbsp;'decrement'})}}>-1
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;当前的值是:{count}
&nbsp;&nbsp;&nbsp;&nbsp;</button&nbsp;onClick={()&nbsp;=></button&nbsp;onClick={()&nbsp;=></div>)
}

Image [5].png

七、其他hooks

查看官方文档 自定义Hook其他Hook

八、使用自定义Hook封装axios

  1. 首先利用自定义Hooks 实现一个fetchApi,传入一个axios和参数params,返回结果data

function&nbsp;useFetch(fetch,&nbsp;params)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;[data,&nbsp;setData]&nbsp;=&nbsp;useState({});
&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;fetchApi&nbsp;=&nbsp;useCallback(async&nbsp;()&nbsp;=>&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;res&nbsp;=&nbsp;await&nbsp;fetch(params);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(res.code&nbsp;===&nbsp;1)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setData(res.data);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;},&nbsp;[fetch,&nbsp;params]);
&nbsp;&nbsp;&nbsp;&nbsp;useEffect(()&nbsp;=>&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fetchApi();
&nbsp;&nbsp;&nbsp;&nbsp;},&nbsp;[fetchApi]);
&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;data;
}

提供一个axios方法fetch

export&nbsp;const&nbsp;fetch&nbsp;=&nbsp;params&nbsp;=>&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;url&nbsp;=&nbsp;'baseUrl'&nbsp;+&nbsp;(params);
&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;axios(url).then(res&nbsp;=>&nbsp;res.json());
};

去使用这个useFetch

function&nbsp;Demo()&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;data&nbsp;=&nbsp;useFetch(fetch,&nbsp;{&nbsp;params1:&nbsp;"xxx"&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;<div>得到返回值:{data}</div>;
}

结果发现,这样并不行得通,一下子程序就会陷入死循环,好好分析一下,原来params是一个对象,usecallback会误认为每次params都改变了,导致陷入了一个死循环中。:render -->useEffect-->useCllback--->data改变--->render,那么自然而然就会想到useMemo,它使得数据项真正的改变时候才会使得useCallback重新执行。或者使用JSON.stringify转换params即可。

params&nbsp;=&nbsp;useMemo(()&nbsp;=>&nbsp;(params),&nbsp;[]);

这里有个较为完整的例子,如何使用hooks分页请求表格数据:分页请求

原文链接:juejin.im

上一篇:CSS 选择器 小结
下一篇:全面介绍JavaScript数组方法
相关教程
关注微信

扫码加入 JavaScript 社区

相关文章

首次访问,需要验证
微信扫码,关注即可
(仅需验证一次)

欢迎加入 JavaScript 社区

号内回复关键字:

回到顶部