react Hook
Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。例如, useState 是允许你在 React 函数组件中添加 state 的 Hook。
完全可选的。 你无需重写任何已有代码就可以在一些组件中尝试 Hook。但是如果你不想,你不必现在就去学习或使用 Hook。
100% 向后兼容的。 Hook 不包含任何破坏性改动。
现在可用。 Hook 已发布于 v16.8.0。
只在最顶层使用 Hook,不要在循环、条件判断或者⼦函数中调⽤。
只在 React 函数组件中或者自定义Hook中调用Hook
使⽤ Effect Hook
useEffect 就是一个 Effect Hook,给函数组件增加了操作副作用的能力。它跟 class 组件中的 componentDidMount、componentDidUpdate 和 componentWillUnmount 具有相同的用途,只不过被合并成了一个 API。
跟 useState 一样,可以在组件中多次使用 useEffect
import React, { useState, useEffect } from “react”;
export default function HookPage(props) {
// 声明⼀个叫 “count” 的 state 变量,初始化为0,setConunt为更新count的对应函数,如果定义多个state,则如: const [age, setAge] = useState(42);const [fruit, setFruit] = useState(‘banana’); const [todos, setTodos] = useState([{ text: ‘Learn Hooks’ }]);
const [count, setCount] = useState(0);
// 与 componentDidMount 和 componentDidUpdate相似,return函数返回类似于componentWillUnmount
useEffect(() => {
// 更新 title
document.title = You clicked ${count} times
;
});
return (
HookPage
{count}
effect 的条件执⾏
默认情况下,effect 会在每轮组件渲染完成后执⾏。这样的话,⼀旦 effect 的依赖发⽣变化,它就会被
重新创建。
然⽽,在某些场景下这么做可能会矫枉过正。⽐如很多时候,我们不需要在每次组件更新时都创建新的订阅,⽽是仅需要在指定值改变时才需要重新创建。
要实现这⼀点,可以给 useEffect 传递第⼆个参数,它是 effect 所依赖的值数组。
示例如下:
import React, { useState, useEffect } from “react”;
export default function HookPage(props) {
// 声明⼀个叫 “count” 的 state 变量,初始化为0
const [count, setCount] = useState(0);
const [date, setDate] = useState(new Date());
// 与 componentDidMount 和 componentDidUpdate相似
useEffect(() => {
// 更新 title
document.title = You clicked ${count} times
;
}, [count]);//仅在count发生改变时执行更新
useEffect(() => {
const timer = setInterval(() => {
setDate(new Date());
}, 1000);
}, []);//传入空数组表示只执行一次,更新不受任何数据影响
return (
HookPage
{count}
清除 effect,React 会在组件卸载的时候执行清除操作。
通常,组件卸载时需要清除 effect 创建的诸如订阅或计时器 ID 等资源。要实现这⼀点, useEffect
函数需返回⼀个清除函数,以防⽌内存泄漏,清除函数会在组件卸载前执⾏。return后面返回的既清除函数
useEffect(() => {
const timer = setInterval(() => {
setDate(new Date());
}, 1000);
return () => clearInterval(timer);
}, []);
自定义Hook
⾃定义 Hook 是⼀个函数,其名称以 “use” 开头,函数内部可以调⽤其他的 Hook。
import React, { useState, useEffect, useMemo } from “react”;
export default function CustomHookPage(props) {
//定义⼀个叫count的state变量,初始化为0
const [count, setCount] = useState(0);
//和didMount、didUpdate类似
useEffect(() => {
console.log(“count effect”);
// 只需要在count发⽣改变的时候执⾏,传入参数count
document.title = 点击了${count}次
;
}, [count]);
return (
⾃定义Hook
{count}
useMemo与useCallback(useCallback(fn, deps) 相当于 useMemo(() => fn, deps))
useMemo
把“创建”函数和依赖项数组作为参数传⼊ useMemo ,它仅会在某个依赖项改变时才重新计算
memoized 值。这种优化有助于避免在每次渲染时都进⾏⾼开销的计算。
import React, { useState, useMemo } from “react”;
export default function UseMemoPage(props) {
const [count, setCount] = useState(0);
const expensive = useMemo(() => {
console.log(“compute”);
let sum = 0;
for (let i = 0; i < count; i++) {
sum += i;
}
return sum;
//只有count变化才重新执⾏
}, [count]);
const [value, setValue] = useState("");
return (
UseMemoPage
expensive:{expensive}
{count}
useCallback
把内联回调函数及依赖项数组作为参数传⼊ useCallback ,它将返回该回调函数的 memoized 版本,
该回调函数仅在某个依赖项改变时才会更新。当你把回调函数传递给经过优化的并使⽤引⽤相等性去避
免⾮必要渲染(例如 shouldComponentUpdate )的⼦组件时,它将⾮常有⽤。
import React, { useState, useCallback, PureComponent } from “react”;
export default function UseCallbackPage(props) {
const [count, setCount] = useState(0);
const addClick = useCallback(() => {
let sum = 0;
for (let i = 0; i < count; i++) {
sum += i;
}
return sum;
//只有count变化,这⾥才重新执⾏
}, [count]);
const [value, setValue] = useState("");
return (
UseCallbackPage
{count}
useRef
const refContainer = useRef(initialValue);
useRef 返回一个可变的 ref 对象,其 .current 属性被初始化为传入的参数(initialValue)。返回的 ref 对象在组件的整个生命周期内保持不变。
一个常见的用例便是命令式地访问子组件:
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// current
指向已挂载到 DOM 上的文本输入元素
inputEl.current.focus();
};
return (
<>
Focus the input
</>
);
}
本质上,useRef 就像是可以在其 .current 属性中保存一个可变值的“盒子”。