最近组内大佬给小编安排了一个任务,整理一份关于react-hooks
的简单的使用教程。
看到消息的时候有点欣喜,因为小编之前就想整理,奈何抵不过惰性,正好趁着这次机会,把hooks
相关的知识点学习并巩固一下(虽然没有用过,但是期待在以后的项目中尝试)。
这篇文章会围绕hook
的特性以及基本使用方式,State hook
,Effect hook
,useContext
以及自定义hook展开,有兴趣的童鞋可以停下脚步,稍微了解一下。
Hook是什么?
首先,我们需要知道的是,hook
是react
16.8的新增特性,他的最特别的一点就是命名的时候一定要以use
开头。不要问我为什么,可能是为了便于识别是否为hooks
的一种手段吧。
在这里,不得不说一下linter
这个插件,这是专门用来检查hook格式的一个插件,pick它吧。
其次,hook
是一些可以让你在函数组件内“钩入” React state
以及生命周期等特性的函数。(例如,useState
是允许你在 React
函数组件中添加 state
的 Hook
)需要注意的是,hook
不能在class
组件中使用。
最后,hook
100%向下兼容。hook也可以将组件中相互关联的部分拆分成更小的函数(比如设置订阅,请求数据等)。
⚠️hook
就是javascript
函数,但是使用其有两个额外的原则:
1.只能在函数最外层调用hook
;
2.只能在react
的函数组件中调用hook
;
3.不要在循环,条件或嵌套以及普通的javascript
中调用hook
;
基本用法
首先,看官网给的一个比较简单的计数器示例:
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>
);
}
可以看到,它引用了useState
,然后声明了一个初始值为0的count
的变量,通过setCount
这个方法去直接改变count
的值。
这里的onClick={() => setCount(count + 1)}
在没有hook
之前,可以理解为onClick={this.setcount}
,然后在声明的setCount
方法内进行this.setState({count: count + 1})
操作。
以上可以看出,使用hook
方便很多,在精简了代码的基础上,还可以去掉传统this指向等繁琐的问题。
我们再来看另一个配合ant design使用的例子:
function Example() {
const [open, setOpen] = useState(false);
return (
<>
<Button type="primary" onClick={() => setOpen(true)}>
Open Modal
</Button>
<Modal
visible={open}
onOk={() => setOpen(false)}
onCancel={() => setOpen(false)}
/>
</>
);
}
拓展一下,我们可以通过给按钮设置初始值来控制弹窗的弹出与关闭等等。
State hook
使用useState
时,我们需要知道的总结一下大概有以下几点:
1.state
是只读的;
2.useState
函数的第一个参数,是state
变量的初始值;
3.每次进行渲染时,多个State Hook
的顺序、数量都必须一样;
4.state
变量发生变化时,会触发新的渲染;state
变量没变化,不触发新的渲染;
使用state hook
的主要作用就是获取需要的 state
和 更新state
的方法,以官网例子为例:
const [state, setState] = useState(initialState);
参数: initialState
可以作为state
的一个初始值,同时,他也可以是一个函数,返回值作为state
的初始值,该参数只会在初始渲染时起作用。
返回值: 会返回一个数组,一个是state
的值,另一个是更新state
值的一个方法。(这里的setState
不会合并state
的值)。
如果要定义多个变量,可以多次使用useState
。
小编觉得,官网关于state hook
部分有一个小技巧值得一提,如下:
var fruitStateVariable = useState('banana'); // 返回一个有两个元素的数组
var fruit = fruitStateVariable[0]; // 数组里的第一个值
var setFruit = fruitStateVariable[1]; // 数组里的第二个值
使用数组解构来将定义的变量通过[0],[1],解构到原来setState
返回的两个值的数组当中。
Effect hook
数据获取,设置订阅以及手动更改 React
组件中的 DOM
都属于副作用。Effect Hook
可以让我们在函数组件中通过使用useEffect
提升操作副作用的能力,它在渲染之后执行,我们可以把它看做 componentDidMount
,componentDidUpdate
和componentWillUnmount
这三个函数的组合。
简单的理解就是:useEffect
跟以上三个生命周期用途相同,只不过被合并成了一个API
。
在使用useEffect
的时候,需要考虑两点:需要清除和不需要清除。那么什么情况下需要清除,什么情况下又不需要清除呢?小编带你来了解
需要清除副作用的情况
当订阅外部数据源的时候,需要清除数据。数据需要清除,可以返回一个函数。
以官网的例子为例:
import React, { useState, useEffect } from 'react';
function FriendStatus(props) {
const [isOnline, setIsOnline] = useState(null);
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
// Specify how to clean up after this effect:
return function cleanup() {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});
if (isOnline === null) {
return 'Loading...';
}
return isOnline ? 'Online' : 'Offline';
}
为了不会引起内存泄漏,订阅外部数据源时需要清除数据。effect
返回一个函数是因为这是effect
可选的清除机制,同时,React
会在组件卸载的时候执行清除操作。
当然,在hook
还没出来之前,使用class创建组件订阅外部数据源时,清除方式如下(此处依旧引用官网代码):
class FriendStatus extends React.Component {
constructor(props) {
super(props);
this.state = { isOnline: null };
this.handleStatusChange = this.handleStatusChange.bind(this);
}
componentDidMount() {
ChatAPI.subscribeToFriendStatus(
this.props.friend.id,
this.handleStatusChange
);
}
componentDidUpdate(prevProps) {
// 取消订阅之前的 friend.id
ChatAPI.unsubscribeFromFriendStatus(
prevProps.friend.id,
this.handleStatusChange
);
// 订阅新的 friend.id
ChatAPI.subscribeToFriendStatus(
this.props.friend.id,
this.handleStatusChange
);
}
componentWillUnmount() {
ChatAPI.unsubscribeFromFriendStatus(
this.props.friend.id,
this.handleStatusChange
);
}
handleStatusChange(status) {
this.setState({
isOnline: status.isOnline
});
}
render() {
if (this.state.isOnline === null) {
return 'Loading...';
}
return this.state.isOnline ? 'Online' : 'Offline';
}
}
当使用class创建一个组件,并且使用了componentDidMount
订阅,在componentDidUpdate
中更新订阅,在componentWillUnmount
清除。
可以看到,较使用effect
来说,class
清除副作用时代码较为冗余。
无需清除副作用的情况
当effect
内只有单行代码,或者我们只想在React
更新DOM
之后运行一些额外的代码。比如发送网络请求,手动变更 DOM
,记录日志,这些无需清除的操作。
如官网代码所示:
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `You clicked ${count} times`;
});
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
这种情况下无需清除副作用。
还有一点我们应该要注意的是,我们需要跳过一些不必要副作用函数。那么,如何才能跳过呢?这里就要讲到useEffect
的第二个参数。
我们需要给useEffect
的传入第二个参数,当第二个参数改变时,执行副作用;当参数不改变时,可直接跳过副作用不执行。例子如下:
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]); // 当count的值更新时,才会重新执行`document.title`
useContext的使用
我们先来看个例子:
import React from 'react';
const appContext = React.createContext(); //创建一个context
functuion App() {
return (
<appContext.Provider value={888}> //这使用了Provider主要是为所有后代提供value值
<div>
<Show />
</div>
</appContext.Provider>
)
}
function Show() {
return (
<appContext.Consumer> //使用Consumer获取到value的值
{value => <div>{value}</div>}
</appContext.Consumer>
)
}
首先我们可以看到,使用contextAPI
的方式下,可以通过创建一个context
上下文,返回一个带有{Provider, Consumer}
的值的对象,然后通过使用Provider
主要是为所有后代提供value
值,这避免了以往手动一个个向子孙组件传递prop
。
然后,可以使用Consumer
可以从上下文获取value的值。需要注意的一点是,这个Consumer
子组件,在这里只能是唯一的一个函数。
再来看一下使用useContext的情况
import React, { useContext } from 'react';
const appContext = React.createContext(); //创建一个context
functuion App() {
return (
<appContext.Provider value={888}> //这使用了Provider主要是为所有后代提供value值
<div>
<Show />
</div>
</appContext.Provider>
)
}
function Show() {
const apps = useContext(appContext);
return (
{value => <div>{apps}</div>}
)
}
这里调用useContext
,传入从React.createContext()
获取到的上下文对象。需要注意的一点是,useContext
的参数必须是 context
对象本身,即useContext(appContext)
,而不是useContext(appContext.Provider)
或useContext(appContext.consumer)
。
自定义hook
前面已经讲到,hook
一般以use
开头,自定义hook也一样。自定义 hook
是一个函数,其名称以 use
开头(必须),函数内部可以调用其他的hook
。
当项目中,我们想要共享组件之间的状态逻辑时,最常见的方法是使用高阶组件
或者 render props
。但是,使用自定义的hook
可以是你在不增加组件的前提之下,把公用逻辑提取出来进行复用。
(详情可查看官网:react.docschina.org/docs/hooks-…)
以上就是小编这次对于react-hooks知识点的一个大概的梳理,若有错误,欢迎指出,一起学习,一起进步。(若觉尚可,可暗戳戳笔芯)