自从React提出Hook这个概念到如今已经有一年多的时间了,在Reactv16.8.0版本中正式发布,到目前为止,Hook的特性已经比较稳定,同时Hook的出现也解决了咱们长期使用React所带来的的一些痛点,最近在项目中初步接触到React Hooks,因此借着这个时间来细致的学习一下React Hooks。react
你还在为该使用无状态组件(Function)仍是有状态组件(Class)而烦恼吗? ——拥有了hooks,你不再须要写Class了,你的全部组件都将是Function。设计模式
你还在为搞不清使用哪一个生命周期钩子函数而日夜难眠吗? ——拥有了Hooks,生命周期钩子函数能够先丢一边了。数组
你在还在为组件中的this指向而晕头转向吗? ——既然Class都丢掉了,哪里还有this?你的人生第一次再也不须要面对this。bash
咱们常写的React组件通常分为两种,有状态组件和无状态组件,即Class组件和函数组件,函数组件相对于Class组件来讲,主要具备这些特性:app
从这些点来看,函数组件无疑是更好的选择,没有任何反作用,组件是可预测的,但在实际开发中,咱们由于复杂的业务逻辑而不得不选择Class组件,而且随着项目的不断维护更改,整个组件内部的逻辑也会愈来愈复杂,代码愈来愈臃肿,因此这个时候的React Hooks出现了,Hook 是 React 16.8 的新增特性。它可让你在不编写 class 的状况下使用 state 以及其余的 React 特性。,这是官方对Hook的解释,按照我本身的理解,它的出现,使得咱们可以在函数组件的基础上,维护一个state,而且可以代替咱们须要在生命周期中所完成的一些操做。dom
首先来看一下官方示例ide
import React, { useState } from 'react';
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>
);
}
}
复制代码
import React, { useState } from 'react';
function Example() {
// 声明一个叫 "count" 的 state 变量
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
复制代码
整段代码是否是简洁了不少!组件从Class组件变成函数组件,而且在使用useState这个hook之后,拥有了本身的状态,还能够经过setCount随时改变改变本身的状态。固然,React Hooks除了useState这个hook之外,还有包括useEffect(提供相似生命周期函数的做用), useContext(提供上下文的功能)等一系列其余hook,而且还能够自定义hook来知足咱们不一样的需求。函数
React在组件层面作到了高内聚,低耦合,组件能够说是react的一个核心,一个页面由大大小小不一样的许多个组件构成,可是在一个实际项目中,一个组件的代码实际上是很长的,加上组件自己的state和声明周期函数中的一些操做,使得咱们真正去复用一个组件变的不是很简单,以前官方推荐的解决方法是渲染属性(Render Pros)和高阶组件(HOC)学习
渲染属性指的是使用一个值为函数的 prop 在 React 组件之间的代码共享。说白了就是经过传递props给一个组件之后返回一个组件给咱们,就像这样 :ui
<DataProvider>
<Component data={data} />
</DataProvider>
复制代码
至于高阶组件其实就是一个函数,一个以组件为参数,而且返回一个新的组件的函数。说白了就是一个函数对传入的组件进行了处理之后在返回,就像这样
function logProps(InputComponent) {
InputComponent.prototype.componentWillReceiveProps = function(nextProps) {
console.log('Current props: ', this.props);
console.log('Next props: ', nextProps);
};
// 返回原始的 input 组件,暗示它已经被修改。
return InputComponent;
}
// 每次调用 logProps 时,加强组件都会有 log 输出。
const EnhancedComponent = logProps(InputComponent);
复制代码
这两种模式乍看起来确实以为不错,做为一个小白,说实话我一直写的也挺爽的,可是使用这两种设计模式,会不可避免的增长咱们的代码层级,致使组件嵌套过深 ,随便打开一个网页都能看到这种嵌套很深的dom结构,因此随着今天的深刻,也发现了这两种模式并不是最优解。
写了也有接近半年的React了,可是直到如今,除了最经常使用的几个生命周期函数之外,每次用到其余的生命周期函数,我都要去查一下生命周期表单来确认有没有写错,更不用说复杂组件中生命周期函数的使用了,咱们须要在生命周期中实现获取数据,监听事件,取消订阅等等一系列的逻辑,随着组件的不断维护扩充,组件也就变得愈来愈难以理解
class中最难以理解的应该是this了吧,为了保证this的指向符合预期,this.funtion = this.funtion.bind(this)写了无数遍, <div onClick={(data) => this.function(data)}> </div>
也常常会写,数不清的this写起来确实很麻烦
为了消灭这些开发过程当中的问题,Hooks来了!(他来了他来了,他踩着祥云走来了!)
export default function CountDown(props) {
const tmr = useRef(0);
const [stop, setStop] = useState(false);
const [stopTime, setStopTime] = useState(0);
const [refresh, setRefresh] = useState(props.refresh);
const handleRefresh = useCallback(() => {
clearTimeout(tmr.current);
tmr.current = setTimeout(() => {
setRefresh(refresh - 1);
if (refresh == 0) {
const { onFinish } = props;
onFinish && onFinish();
setRefresh(props.refresh);
}
}, 1000);
}, [setRefresh, refresh, props]);
const handleClick = useCallback(() => {
if (!stop && refresh > 0) {
clearTimeout(tmr.current);
setStop(true);
setStopTime(refresh);
} else if (stop) {
setStop(false);
// setRefresh(stopTime);
handleRefresh();
}
}, [stop, refresh, stopTime, setRefresh]);
useEffect(() => {
handleRefresh();
}, [refresh]);
return (
<div className={styles.wrapper}>
{
stop ? (
<div onClick={handleClick} >
{
stopTime < 10 ? '0' + stopTime : stopTime
}
</div>
) : (
<div onClick={handleClick}>
{
refresh < 10 ? '0' + refresh : refresh
}
</div>
)
}
</div>
);
}
复制代码
这是一个倒计时组件,当倒计时结束时会调用回调函数,执行而后从新计时,让咱们一块儿看一下它是怎么实现的吧: 首先,声明咱们须要维护的变量stop、stopTime、refresh,声明tmr时注意,若是咱们要声明一个普通变量,最好使用useRef来声明,声明之后初始值会存储在tmr.current中,之后每次修改也都是修改tmr.current,这样作的好处是可以确保tmr不会丢失,由于整个函数每次都会彻底从新渲染,因此很容易形成咱们最新渲染出来的变量没有指向先前的修改从而致使bug的产生, 接下来,使用useCallback来声明两个咱们须要用到的函数分别用来处理点击事件和回调事件。useCallback(() => {},[]), []中必定要将咱们函数中须要用到的依赖也就是用到的变量声明,这里的目的也是为了数据的指向精准,最后声明useEffect(() => {},[]) []这里的数组里面须要写什么呢? 你但愿当哪一个变量发生变化时执行这个useEffect呢? 但愿哪一个就写入哪一个, 若是写入一个空数组,那么做用就至关于componentDidMount只会执行一次,若是像这样什么也没有写,useEffect(() => {}),那么每次组件从新渲染时,这个useEffect都会执行
总结,React Hooks的出现,极大的解决了咱们目前在用react实现复杂项目时的一些痛点,可是里面的坑也比较多,这篇文章只是初步科普一下React Hooks的使用,至于更深一步好比为何会有精准依赖,Hooks的数据是怎么保持的等等要抽出更多的时间来了解Hooks的源码实现,未完待续~