React中Hook学习
React中Hook学习
Hook 是 React 16.8 的新增特性。可以在编写函数式组件时使用state以及react中的其他特。
hook为了解决react中原有的问题
- 组件之间复用状态逻辑很难
- 复杂组件变得难以理解(例如组件中生命周期被拆分开来写,但是其中的代码相互关联,使得代码变得复杂)
- 类组件中,this工作方式复杂,并且在实际项目中存在难以优化的情况。
Hook使用规则
- 只能在函数最外层调用Hook。不要在循环、条件判断或者子函数中调用。
- 只能在React的函数组件中调用Hook。不要在其他JavaScript函数中调用。(还有一个地方可以调用Hook——就是自定义的Hook中)
使用linter插件来强制执行这些功能
linter插件下载地址:https://www.npmjs.com/package/eslint-plugin-react-hooks
Hook是什么
Hook是一个特殊的函数,它可以让你“钩入”React的特性。例如,useState是允许你在React函数式组件中添加state的Hook
State Hook
函数式组件使用useState来实现类组件中state状态
import React, { useState } from 'react';
export default function CountHook() {
// 声明一个叫 “count” 的 state 变量,并且setCount为更改state变量的方法。
// 并且只初始化一次,重新渲染时保留之前的值
// useState()唯一的参数为初始化state的值(不一定要是对象,this.state中必须是对象形式)
// 如果想要在state中存储两个不同的变量,只需要调用useState(0两次即可。)
// 方括号表示js中的数组解构
const [count, setCount] = useState(0);
return (
<div>
// 获取state中的count
<p>You clicked {count} times</p>
// 更新state中的count
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
类组件写法
import React, { Component } from 'react'
export default class Count extends Component {
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>
)
}
}
useEffect
在函数式组件中使用useEffect来控制副作用
const [isOnline, setIsOnline] = useState(null);
useEffect(() => {
// 组件跟新(第一次挂载)的时候调用
// 创建函数,并添加订阅
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
// 组件卸载的时候调用,用于清除订阅(只有当副作用需要清除的时候才添加返回函数)
return ()=>{
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});
在类式组件中使用生命周期函数控制副作用
// 组件挂载的时候调用
componentDidMount() {
ChatAPI.subscribeToFriendStatus(
this.props.friend.id,
this.handleStatusChange
);
}
// 组件卸载的时候调用
componentWillUnmount() {
ChatAPI.unsubscribeFromFriendStatus(
this.props.friend.id,
this.handleStatusChange
);
}
// 租价更新的时候调用
handleStatusChange(status) {
this.setState({
isOnline: status.isOnline
});
}
-
传递给useEffect的函数在没词渲染中都会有所不同,正是因为这样可以在effect中获取最新的count的值,二不用担心其过期。
-
与 componentDidMount或componentDidUpdate 不同,使用 useEffect调度的 effect 不会阻塞浏览器更新屏幕,这让你的应用看起来响应更快。
-
useEffect在浏览器渲染完成后执行,useLayoutEffect在浏览器渲染前执行,因此在涉及更改布局的effect最好使用useLayoutEffect。
-
useEffect可以允许我们按照代码的用途分离他们, 而不是像生命周期函数那样。
-
useEffect中effect在每次新的effect之前,对前一个effect进行清理
通过设定观测特定值的变化来决定是否调用effect来进行性能的优化
// 仅在 count 更改时更新
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]);
// 仅在 props.friend.id 发生变化时,重新订阅
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
}, [props.friend.id]);
Hook规则
linter插件可以强制执行这些规则
- 只在最顶层使用Hook
不在循环,条件或者潜逃函数中使用Hook
- 只在React函数中调用Hook
只在React的函数组件中调用Hook,只在自定义Hook中调用其他Hook
ESLint插件使用
npm install eslint-plugin-react-hooks --save-dev
自定义Hook
提取多个组件之间重复的逻辑并复用
原:
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);
return () => {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});
//--------------------------------
if (isOnline === null) {
return 'Loading...';
}
return isOnline ? 'Online' : 'Offline';
}
import React, { useState, useEffect } from 'react';
function FriendListItem(props) {
//重复部分
//--------------------------------
const [isOnline, setIsOnline] = useState(null);
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});
//--------------------------------
return (
<li style={{ color: isOnline ? 'green' : 'black' }}>
{props.friend.name}
</li>
);
}
提取自定义Hook
使用use开头的函数
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替换重复部分
function FriendStatus(props) {
const isOnline = useFriendStatus(props.friend.id);
if (isOnline === null) {
return 'Loading...';
}
return isOnline ? 'Online' : 'Offline';
}
function FriendListItem(props) {
const isOnline = useFriendStatus(props.friend.id);
return (
<li style={{ color: isOnline ? 'green' : 'black' }}>
{props.friend.name}
</li>
);
}
在两个组件中使用相同的state并不会共享state和副作用

浙公网安备 33010602011771号