一、作用:
Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。
二、规则
只能在函数最外层调用 Hook。不要在循环、条件判断或者子函数中调用。 只能在 React 的函数组件中调用 Hook。不要在其他 JavaScript 函数中调用。
三、用法
1、useState
import React,{useState} from "react";
const [state, setState] = useState(initialState);
调用useState 返回一个 包含state以及更新 state 函数的数组
在初始化时,返回的state 与传入的initialState 值相同。
setState 函数用于更新 state,接收一个新的 state 值并将组件重新渲染。
setState(newState); 在后续的重新渲染中,useState 返回的第一个值将始终是更新后最新的 state。
下面介绍一下setState的两种用法,函数组件中的调用也如类组件中this.setState一样有两种写法。
方法一:调用setState,参数为想要更新的结果,这是我们使用useState常用的更新方法。
方法二:参数传如一个更新函数,函数的参数为上一次更新后的结果。
下面进行两种方法的示例展示。
const Counter = () => {
const [count, setCount] = useState(0);
const changeCount = () => {
setCount(count + 1)
}
const addCount =() =>{
setCount((prevCount) => {
return prevCount + 1
})
}
return (
<div>
{count}
<button onClick={changeCount}>+</button>
<button onClick={addCount}>+</button>
</div>
);
}
这样看起来两者似乎并没有什么不同,实现的结果也是相同的,但是我们只要稍加变化,将其包装成异步,这时候两种不同的写法却有了不同的实现结果。
const Counter = () => {
const [count, setCount] = useState(0);
const changeCount = () => {
setTimeout(() => {
setCount(count + 1)
}, 3000);
}
const addCount =() =>{
setTimeout(() => {
setCount((prevCount) => {
return prevCount + 1
})
}, 3000);
}
return (
<div>
{count}
<button onClick={changeCount}>+</button>
<button onClick={addCount}>+</button>
</div>
);
}
如例所示,我们在定时器3s的时间内进行连续点击,第一种写法只更新了一次,结果是1,因为定义的count并没有发生变化。
而第二种写法的结果一直保持是最新的,因为在进行更新时并不是以初始化定义的count进行加1操作,而是已上一次更新之后的结果为基础,这样保持了count的实时性。
两种写法和类组件中的setState相似,可以直接进行更新,也可以进行函数式更新。
const changeCount = () => {
this.setState({count:this.state.count+1})
}
const addCount = () => {
this.setState((prevState)=>{
count:prevState.count+1
})
};
另外有一点,class组件中setState更新数据是进行合并,而hooks中更新是进行数据的替换。
constructor(props) {
super(props);
this.state = { count: 0, name: \'58\' };
}
this.setState((state) => {
return {count: state.count + 1}
})
// 运行一次后的state结果是{ count: 1, name: \'58\' }
2、useEffect
副作用钩子,常在此钩子中进行数据获取,发布订阅。
可以把useEffect看成类组件中componentDidMount,componentDidUpdate和componentWillUnmount三个生命周期的组合。
对于useEffect的常用方法一般有两种,一种为需要清除和不需要清除的。
不需要清除 如例所示,我们使用hooks实现一个把当前点击数量展示到document的title中的功能。
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>
);
}
这样的写法使我们的effect在每次组件重新渲染时都会进行执行一次,让我们不需要考虑当前是挂载还是更新阶段。
在类组件中我们需要将这些副作用函数分别在componentDidMount和componentDidUpload两个生命周期函数中进行重复使用,这样才能保证实现点击次数的实时更新。
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
componentDidMount() {
document.title = `You clicked ${this.state.count} times`;
}
componentDidUpdate() {
document.title = `You clicked ${this.state.count} times`;
}
render() {
return (
<div>
<p>You clicked {this.state.count} times</p>
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
Click me
</button>
</div>
);
}
}
相比之下useEffect看起来更加简洁,也减少了一些重复代码的书写。
需要清除 在平时的工作中,我们可能会在代码中进行发布订阅操作,为了防止内存泄漏,常用的方法就是在componentDidMount中进行订阅,在componentWillUnmount中进行清除订阅。
componentDidMount() {
ChatAPI.subscribeToFriendStatus(
this.props.friend.id,
this.handleStatusChange
);
}
componentWillUnmount() {
ChatAPI.unsubscribeFromFriendStatus(
this.props.friend.id,
this.handleStatusChange
);
}
而在hooks中我们只需要在useEffect钩子中返回一个清除函数,那么react就会在组件卸载的时候执行当前函数,进行清除操作。
useEffect(() => {
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
})
上面两个例子的effect在每次组件重新渲染时都会重新执行,这样的话就会造成一些不必要的调用或者渲染,接下来我们对上面的例子做一些优化,使其能够在effect中依赖项未发生变化时不重新执行。
import React, { useState, useEffect } from \'react\';
function Example() {
const [count, setCount] = useState(0);
const [number, setNumber] = useState(0);
useEffect(() => {
document.title = `You clicked ${count} times`;
},[count]);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
<button onClick={() => setNumber(number + 1)}>
Click me
</button>
</div>
);
}
在useEffect的第二个参数中,我们可以加入一个deps数组,当数组里的任意一项发生变化时,再重新执行当前的effect,这样就避免了一些不必要的调用和渲染。
我们在调用setNumber时,count并未发生变化,所以document的title不需要进行变化,effect也就不需要重新执行。
但当我们不把count加入依赖数组时,number的每一次变化也会使得effects重新进行一次调用,而这一步是我们所不想看到的。
将count加入依赖数组时,每次组件重新渲染时,effect会判断当前是否发生变化,发生改变时才会重新执行当前effect,这一步我看来更像在shouldComponentUpdata中进行pervState.count===state.count判断一样。
3、自定义hook 在使用函数组件时,难免会遇到两个组件之间存在共同逻辑。将相同逻辑拷贝在不同组件中,这未免不是一种低效的办法,这时候就轮到自定义hook发挥作用的时候了。
通过对相同逻辑hook的封装,我们即可实现高效的办公效率和减少一些重复代码。
自定义规则 自定义hook是一个函数,名称必须以use开头,函数内部可以调用其他的hook。
在自定义hook中我们可以制定想要接受的参数,和在当前hook中需要做什么,以及hook返回的结果,这些规则都可以根据我们的需求进行改变。
import { useState, useEffect } from \'react\';
function useFriendStatus(friendID) {
const [isOnline, setIsOnline] = useState(null);
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
};
});
return isOnline;
}
以上面代码为例,我们想要在不同组件中获取不同用户的登录状态,并在当前组件卸载是进行清除。对此我们把获取在线状态的hook提取出来,在组件中进行调用,只需要将当前用户ID传递就可接受一个是否在线的返回结果。
function FriendStatus(props) {
const isOnline = useFriendStatus(props.friend.id);
return isOnline ? \'Online\' : \'Offline\';
}
四、小结
尽管在写这篇文章之前就已经使用过很长一段时间的hooks,但在写的时候依旧有一些不知从何处写起的感觉,还是对一些内部的实现原理说的不是很清楚。通过这次对官方文档以及相关文章的阅读,也是加强了一些对hooks逻辑整理,文章如有观点偏差,还请大家指正。
革命尚未成功,同志仍需努力.
最后推荐一篇对于了解hook实现很有帮助的文章。overreacted.io/a-complete-…
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!