Hook 是一些可以让你在函数组件里“钩入” React state 及生命周期等特性的函数。Hook 不能在 class 组件中使用 —— 这使得你不使用 class 也能使用 React。
【所以,hook的出现就是为了干掉class,因为class里面的state无法复用,导致了很多无用功】
useState
import React, { useState } from 'react';
function Example() {
// 这是一个“函数组件”,或称为无状态组件
const [count, setCount] = useState(0);
// 声明一个叫 “count” 的 状态 变量,并将其设为0。
// 初始值,可以是个变量传入,如props传入
// 可以调用setCount来更新count
// 想要多个变量,就多声明几个useState
const [fruit, setFruit] = useState('banana');
const [todos, setTodos] = useState([{ text: '学习 Hook' }]);
// 这些状态都能单独更新
return (
<div>
<p>You clicked {count} times</p> // 这里直接能用count
<button onClick={() => setCount(count + 1)}> // 直接用状态对应的更新函数
Click me
</button>
</div>
);
}
useState
,给函数添加了一个内部的state。返回[ 当前状态 ,更新状态的函数 ]
设定的初始值,可以不是对象。例子中的是数字。
更新函数类似this.setState
useState 不会自动合并更新对象。【?】
hook是特殊的函数,能引入react的特性。如添加state
react怎么知道,哪个state对应哪个useState?
靠的是 Hook 调用的顺序
useState useEffect
一组一个逻辑,以此来更新
useEffect
副作用(Side Effect)是指,函数或者表达式的行为,会交互到外部世界
Effect Hook 为你的函数式组件增添了执行 side effects 的能力。
它跟 class 组件中的 componentDidMount
、componentDidUpdate
和 componentWillUnmount
具有相同的用途
【感觉就是,组件和外界的交互,全部都放在了这个useEffect()里面了】
不需要清理的side effects
import { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
// Similar to componentDidMount and componentDidUpdate:
useEffect(() => {
// 每次渲染后(第一次渲染+数据更新)
// 可以理解为,这个函数里面内容,是渲染的一部分
document.title = `You clicked ${count} times`; // 可以读取最新的count
});
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
在 React 类组件中,render
方法不应该引起 side effects 。 现在还为时过早——我们通常希望在 React 更新 DOM 之后, componentDidMount
和 componentDidUpdate
中,执行我们的 side effects 。
useEffect不会阻止浏览器更新屏幕,是一个异步的过程【?】 如果想要同步更新,可以用useLayoutEffect
需要清理的side effects
比如我们我们可能希望设置对某些外部数据源的订阅,这就需要清理,不然内存就装不下。 如果你的 effect 返回一个函数,React 将在清理时运行它:
import { useState, useEffect } from 'react';
function FriendStatus(props) {
const [isOnline, setIsOnline] = useState(null);
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
useEffect(() => {
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
// 清理的时候运行return函数
// 如组件卸载、每次渲染之前
return function cleanup() {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});
if (isOnline === null) {
return 'Loading...';
}
return isOnline ? 'Online' : 'Offline';
}
忘记正确处理 componentDidUpdate 是 React 应用程序中常见的 bug 漏洞。 比如props在组件出现在屏幕上时发生了变化,就会有个错误的在线记录,以及错误的卸载对象。 因此还要增加一个DidUpdate
可以将不相关的逻辑分成不同的 effects :
function FriendStatusWithCounter(props) {
const [count, setCount] = useState(0);
// 第一个
useEffect(() => {
document.title = `You clicked ${count} times`;
});
const [isOnline, setIsOnline] = useState(null);
// 第二个
useEffect(() => {
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
// ...
}
【感觉这样,state和effect就能成为一小块,一小块应对一个逻辑】 能根据代码用途来分个代码
跳过清理来优化性能
类组件中,可以通过在componentDidUpdate 中编写与 prevProps
或 prevState
的额外比较来解决这个问题:【这是啥】
componentDidUpdate(prevProps, prevState) {
if (prevState.count !== this.state.count) {
document.title = `You clicked ${this.state.count} times`;
}
}
hook的做法:
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]); // Only re-run the effect if count changes
将数组作为可选的第二个参数传递给 useEffect ,如果在重新渲染之间没有更改这些值,react就会跳过引用effect 如果count值保持不变,就不会跑这条useEffect 数组中,只要有一个状态不同,就会更新effect
如果仅在装载和卸载时需要重新渲染,则可以将空数组([])作为第二个参数传递
hook规则
eslint插件:eslint-plugin-react-hooks 1. 只在最顶层使用hook 不要在循环,条件或嵌套函数中调用 Hook 确保 Hook 在每一次渲染中都按照同样的顺序被调用,保持hook状态的正确 2. 只在react中调用hook
自定义hook
如果我们想在两个组件当中共享逻辑,就能把它抽象到第三个函数中。 必须以use开头,不然react无法检查里面的hook
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 的顶层无条件地调用其他 Hook。
使用:
function FriendStatus(props) {
// 直接当成函数调用
const isOnline = useFriendStatus(props.friend.id);
if (isOnline === null) {
return 'Loading...';
}
return isOnline ? 'Online' : 'Offline';
}
两个组件使用相同的hook函数,不会共享state
传递信息