欢迎大家前往 腾讯云+社区 ,获取更多腾讯海量技术实践干货哦~
TL;DR
一句话总结 React Hooks 就是在 react 函数组件中,也可以使用类组件(classes components)的 state 和 组件生命周期,而不需要在 mixin、 函数组件、HOC组件和 render props 之间来回切换,使得函数组件的功能更加实在,更加方便我们在业务中实现业务逻辑代码的分离和组件的复用。
本文将从以下几个方面介绍 hooks
Hooks 在解决什么问题 Hooks 的 api 介绍 和如何使用 hooks Hooks 是怎么实现的
💡Hooks 在解决什么问题
React 一直在解决一个问题,如何实现分离业务逻辑代码,实现组件内部相关业务逻辑的复用。
一般情况下,我们都是通过组件和自上而下传递的数据流将我们页面上的大型UI组织成为独立的小型UI,实现组件的重用。但是我们经常遇到很难侵入一个复杂的组件中实现重用,因为组件的逻辑是有状态的,无法提取到函数组件当中。这在处理动画和表单的时候,尤其常见,当我们在组件中连接外部的数据源,然后希望在组件中执行更多其他的操作的时候,我们就会把组件搞得特别糟糕:
- 难以重用和共享组件中的与状态相关的逻辑,造成产生很多巨大的组件
- 逻辑复杂的组件难以开发与维护,当我们的组件需要处理多个互不相关的 localstate 时,每个生命周期函数中可能会包含着各种互不相关的逻辑在里面。
- 复杂的模式,如渲染道具和高阶组件。
- 由于业务变动,函数组件不得不改为类组件。
这时候,Hooks就派上用场了。 Hooks 允许我们将组件内部的逻辑,组织成为一个可复用的隔离模块。
借用 @Sunil Pai 的两张图来说明这个问题:
从 React Hooks 中体验出来的是 React 的哲学在组件内部的实现,以前我们只在组件和组件直接体现 React 的哲学,就是清晰明确的数据流和组成形式。既可以复用组件内的逻辑,也不会出现 HOC 带来的层层嵌套,更加不会出现 Mixin 的弊端 。
💡Hooks 的 api 介绍 和如何使用 hooks
@dan_abramov 在会议上给我们介绍了 hooks 的三个关键的api,分别是
State Hooks
、
Effect Hooks
、
Custom Hooks(自定义hooks)
📌state Hooks (useState)
useState 这个方法可以为我们的函数组件带来 local state,它接收一个用于初始 state 的值,返回一对变量。 让函数组件拥有自己的组件。
首先如果我们需要用 classes component 实现一个点击按钮 +1 组件应该怎么写呢?
import React from 'react';
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {count: 0};
this.clickBtn = this.clickBtn.bind(this);
}
clickBtn = () => {
this.setState({
count: this.state.count + 1;
});
}
return (
<div>
<p>You clicked {this.state.count} times</p>
<button onClick={this.clickBtn}>
Click me
</button>
</div>
);
}
复制代码
那使用 useState 是怎么样的呢? 可以看见非常清晰明了。
// 一个简单的点击计数
import { useState } from 'react';
function Example() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
复制代码
📌Effect Hooks (useEffect)
Effect Hooks 用于处理一些带有副作用的操作,下面通过监听窗口宽度的变化代码为例,说明 effect hooks 的使用fangfa
import { useState } from 'react';
function windowWidth() {
const [width, setWithd] = useState(window.innerWidth);
useEffect(() => {
const handleResize = ()=>{
setWidth(window.innerWidth);
}
window.addEventListener('resize', handleResize);
});
return (
<p> window width is {width}</p>
)
}
复制代码
useEffect 可以传入第二个操作来避免性能的损耗,如果第二个参数数组中的成员变量没有变化则会跳过此次改变。如何传入一个空数组 ,那么该 effect 只会在组件 mount 和 unmount 时期执行。
import { useState } from 'react';
function windowWidth() {
const [width, setWithd] = useState(window.innerWidth);
useEffect(() => {
const handleResize = ()=>{
setWidth(window.innerWidth);
}
window.addEventListener('resize', handleResize);
}, [width]); // width 没有变化则不处理
return (
<p> window width is {width}</p>
)
}
复制代码
useEffect 中还可以通过让函数返回一个函数来进行一些取消兼容之类的清理操作,比如取消订阅等
import { useState } from 'react';
function windowWidth() {
const [width, setWithd] = useState(window.innerWidth);
useEffect(() => {
const handleResize = ()=>{
setWidth(window.innerWidth);
}
window.addEventListener('resize', handleResize);
return () => {
// 取消监听窗口的宽度变化
window.removeEventListener('resize');
}
});
return (
<p> window width is {width}</p>
)
}
复制代码
如上所示,内置的 React Hooks 如 useState 和 useEffect 充当基本构建块。 我们可以直接在组件中使用它们,或者我们可以将它们组合到自定义Hook中,例如useWindowWidth。使用自定义Hooks感觉就像使用React的内置API一样。
📌Custom Hooks 自定义组件
接着上面的监听窗口大小的代码,我们接着讲自定义 hooks, 证明 react hooks 是怎么使到组件内的逻辑可复用的。
Talk is cheap, show me the code.
// 一个显示目前窗口大小的组件
function responsiveComponent(){
// custom hooks
const width = useWindowWidth();
return (
<p>当前窗口的宽度是 {width}</p>
)
}
复制代码
上面的代码只有几行,非常清晰明了说明了他的作用就是监听当前窗口的变化,这就是Hooks的目标 - 使组件真正具有声明性,即使它们包含状态和副作用。
我们来看看如何实现这个自定义Hook。我们使用React本地状态来保持当前窗口宽度,并在窗口调整大小时使用副作用来设置该状态
import { useState, useEffect} from 'react';
// custom hooks to listen window width change
function useWindowWidth(){
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
const handleResize = ()=>{
setWidth(window.innerWidth);
}
window.addEventListener('resize', handleResize);
}, [width]); // width 没有变化则不处理
return width;
}
复制代码
[ 在线编辑例子]
⚡ React Hooks 的规则
Hooks 是JavaScript函数,但它们强加了两个额外的规则:
- 只能在 顶层 调用Hooks。不要在循环,条件或嵌套函数中调用Hook。
- 仅从React功能组件调用Hooks。不要从常规JavaScript函数中调用Hook。 (还有另一个地方可以调用Hooks——你自己的定制Hooks。)
🔌 其他 Hooks
这里有一些不常用的内置Hook。例如,useContext允许您订阅React上下文而不引入嵌套:
function Example() {
const locale = useContext(LocaleContext);
const theme = useContext(ThemeContext);
// ...
}
复制代码
发现一个很有趣的仓库, react-use , 包含了很多很有趣的自定义hooks
👀hooks 是如何工作的
以下内容翻译自 react-hooks-not-magic-just-arrays .
react hooks 其实只是一个数组,并不是奇妙的魔法。
如何实现
useState()
方法
让我们在这里通过一个例子来演示状态 hooks 的实现如何工作。
首先让我们从一个组件开始:
function RenderFunctionComponent() {
const [firstName, setFirstName] = useState("Rudi");
const [lastName, setLastName] = useState("Yardley");
return (
<Button onClick={() => setFirstName("Fred")}>Fred</Button>
);
}
复制代码