React hooks的深入理解

2019 / 09 / 12

自从hooks出来之后,react的世界可谓是发生了一个很久没有发生过的超级大的变化。最近写了点点hooks,发现确实还是蛮好用的,所以这段时间打算开始入手了,尽量多的用hooks的方式写,当然class生命周期的方式也很优秀。

本文的大多数内容是基于class来讲解hooks的,如果不懂class的人请先学习class写法的React。

hooks的定义和解决的问题

Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。

hooks就是这么单纯,因为function component没有生命周期钩子和state所以使用起来没有class方便,为此react开发出了一套hooks,也就是class能做到的,hooks都能做到。

当然也不止于此

1. 首先hooks并不是和class在表意上是一致的,相反,使用hooks可能需要另一种思路去编写组件。但是hooks依然需要理解props, context和生命周期的机制,因为他依然是建立在这一套概念上的API。

2. 其次hooks解决了组件之间状态和运行机制难以复用的问题

3. hooks还解决了写法上,componentDidmount和componentDidUpadte中写一样的代码,或者componentDidMount上挂载,在unmount的时候移除的这种因为生命周期而把类似代码分割开的问题。

hooks API

首先说一个非常核心的自己对hooks的理解

hooks的api是针对某个状态或者某个属性变化而设定的,而class的生命周期钩子是针对组件变化的时间段而设定的。

如果理解了这一点,那么他的就能非常快的明白hooks API为什么是那个样子了以及应该如何更好的书写hooks。hooks有一系列的API,主要的依然是useState和useEffect。

useState


export default function HooksDemo() {
const [count, setCount] = useState(0)

const handleAdd = () => {
setCount(count + 1)
}

return (
<div className="hooks-demo-page">
<div>{count}</div>
<button onClick={handleAdd}>add</button>
</div>
)
}

hooks的状态一次只能用数组解构的方式设置一个,而class的状态一次设定整个组件的。

可以想象class是设定一个state和一个setState,而hook是通过useState设定成对的'state'和'setState'。因此hooks的setState是接受一个状态值。而class是接受整个状态。

const handleAdd = () => {
setCount(prevCount => {
return prevCount + 1
})
}

hooks的setState和class的setState完全一致,所以hooks中也可以用函数化的写法,而且他的preState是一个state值。这也完全验证了开始讲解API的时候做的理解上的假设。

useEffect

useEffect的理解起来的难度要远大于useState,因为useState并不对生命周期负责,或者说useState是触发生命周期变化的,而useEffect要涉及到处理组件变化的生命周期的各个阶段。

说白了useEffect就是给state变化搽屁股的,除此之外,他还要给props变化搽屁股。

export default function HooksDemo() {
const [count, setCount] = useState(0)

const handleAdd = () => {
setCount(prevCount => {
return prevCount + 1
})
}

useEffect(() => {
console.log(`count变成了: ${count}`)
})

return (
<div className="hooks-demo-page">
<div>{count}</div>
<button onClick={handleAdd}>add</button>
</div>
)
}

还是上边的demo,useEffect接受一个函数,表示当组件的状态和属性发生变化的时候的进行的回掉。所以你上述的effect会在组件挂载以及count发生变化的任何时候执行。

如果只需要在didMount的时候执行,可以穿一个放着true的数组。

useEffect(() => {
console.log('组件加载完成')
}, [true])

为什么要这么写呢,这个true意味着什么呢。后续会给解答。

依然是上边的demo,这回增加了一个fruit的state,这个时候无论fruit还是count发生变化都会触发effect变动。


export default function HooksDemo() {
const [count, setCount] = useState(0)
const [fruit, setFruit] = useState('apple')

const handleAdd = () => {
setCount(count + 1)
}

const handleSelectFruit = (fruit) => {
setFruit(fruit)
}

useEffect(() => {
console.log('count或者fruit变了')
})

return (
<div className="hooks-demo-page">
<div>{count}<button onClick={handleAdd}>add</button></div>
<div>
{fruit}
<button onClick={() => handleSelectFruit('apple')}>apple</button>
<button onClick={() => handleSelectFruit('purple')}>purple</button>
<button onClick={() => handleSelectFruit('peach')}>peach</button>
</div>
</div>
)
}

为了只需要在count变化的时候,effect变化。可以这么写

useEffect(() => {
console.log('count变了,fruit变没变就不归我管了')
}, [count])

这里能看到,useEffect除了接受一个effectHanlder函数之外,还需要接受一个trigger,或者可以理解为类似于observer观察者模式的一种表达方式。

而这个trigger可以是state也可以是props。

所以上述的didMount时候执行,其实是传入一个常量,那么这个effect就只会被执行一次,因为常量不会发生变化的。同理,我们传入一个空,或者false,以及null都可以,不只是true,任何常量都可以。

unmount的可以通过return一个函数来实现

useEffect(() => {
console.log('组件加载完成')
return () => console.log('组件被销毁了')
}, [null])

总而言之,class生命周期是对组件的不同阶段的控制,而effect对生命周期的阐述,更着重表现在属性和状态上,或者可以理解为effect并不非常关心生命周期,他关心的是状态变化带来的影响。

其他的API

关于hooks的API,包括useRef,useContext,总之既然提出了function component能实现class component的任何能力,就要有各种对应的API。但都变化不大。其他的请自行学习。

嗨,请先 登录

加载中...
(๑>ω<๑) 又是元气满满的一天哟