React Hooks
介绍
Hooks 是一类函数,实现在函数组件中使用 React 状态和生命周期(state and lifecycle)。类组件不再是唯一的选择。自定义 hooks 还可以更好地复用状态相关的逻辑。
Hooks are a new addition in React 16.8. They let you use state and other React features without writing a class.
Change
Thinking in terms of state and synchronization with DOM from lifecycles and time
State Hook
useState
,参数为初始化状态,返回状态变量和修改函数。用数组方式接收可以自定义变量和函数名。
import React, { useState } from "react";
function Example() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}
Effect Hook
side effects(或简称为 effects):是指异步获取数据,订阅数据,直接修改 DOM 等,会影响其他组件且不能在渲染中完成的操作。
useEffect(effect, [dependencies]),参数为回调函数。每次渲染后调用。返回值如果是函数,会在下一次触发 effect 前调用,执行清理相关逻辑。
If you’re familiar with React class lifecycle methods, you can think of useEffect Hook as componentDidMount, componentDidUpdate, and componentWillUnmount combined.
一个函数组件内可以使用多个useEffect
,分布实现didMount
, didUpdate
等效果。
import React, { 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 () => {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});
if (isOnline === null) {
return "Loading...";
}
return isOnline ? "Online" : "offline";
}
自定义 Hook
在函数中包装原生 hooks。这个函数就是自定义 hook。
自定义 hook 可以返回有状态的变量,变化后会触发组件刷新(reactive)。
自定义 Hook 的方案不用添加额外组件。复用状态逻辑,传统方案有:高阶组件、render 属性等。
import { useState, useEffect } from "react";
// 定义 hook
function useWindowWidth() {
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
const handleResize = () => setWidth(window.innerWidth);
window.addEventListener("resize", handleResize);
return () => {
window.removeEventListener("resize", handleResize);
};
});
return width;
}
// 使用 hook
function MyResponsiveComponent() {
const width = useWindowWidth(); // Our custom hook
return <p>Window with is {width}</p>;
}
useRef
useRef
返回引用, 属性.current
会指向 DOM 节点。
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// `current` points to the mounted text input element
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
Hooks 的实现和使用规则
实现原理是数组,闭包?
React hooks: not magic, just arrays
-
仅在函数组件 最外层调用,或者自定义 Hooks内调用
-
不要在循环、条件和嵌套函数内调用
Tips:5 Tips to Help You Avoid React Hooks Pitfalls
为什么引入 Hooks
Class Component 过去存在的问题:
-
生命周期方法容易导致,不同逻辑的代码在同一个方法,同一逻辑的代码在不同方法内
-
很难共享状态相关逻辑
-
很难理解复杂的组件,过度耦合
Event listeners 会涉及
componentDidMount
和componentWillUnmount
-
很难理解
this
和函数上下文 -
很难调试和优化
HOC 或者 renderProps 在调试时会出现多余的组件结构
Function Component 的问题:
-
无法利用
shouldComponentUpdate
进行优化,但可以使用React.memo
。类似于React.PureComponent
。 -
useEffect 不如 Class 生命周期那么全面和直观。主要用于同步副作用和状态。
-
学习曲线
React Function Components with hooks vs Class Components
何时使用
建议优先使用函数组件加 hooks,需要较多内部状态和生命周期时考虑使用 Class。