1. useState
useState 函数接收一个参数作为状态的默认值,函数返回结果为一个数组,数组第一项为状态值,第二项是一个可以变更状态值的函数。
import { useState } from "react";
export default function App() {
const [count, setCount] = useState(0);
const onClickButton = () => {
// 第一种方式
setCount(count + 1);
console.log(count); // 值得注意的是,set更新函数是异步的,打印的count仍然更新前的值
// 第二种方式
setCount((prevState) => prevState + 1);
};
return (
<>
<div>{count}</div>
<button onClick={onClickButton}>按钮</button>
</>
);
}
2. useRef
使用 .current 获取DOM节点。需要注意的是,自定义组件要使用 forwardRef 包装,才能使用 useRef。
import { useRef } from "react"
export default function App() {
const inputRef = useRef<HTMLInputElement>(null);
return (
<>
<input ref={inputRef}></input>
<button onClick={() => {
// 获取input输入框并聚焦
inputRef.current?.focus()
}}>按钮</button>
</>
)
}
3. useImperativeHandle
useImperativeHandle 搭配 forwardRef 在自定义组件时,暴露组件的方法供外部调用。
import { useRef, useImperativeHandle, forwardRef } from "react";
const Child = forwardRef((props, ref) => {
const inputRef = useRef<any>(null);
// 暴露方法
useImperativeHandle(ref, () => {
return {
// 使输入框聚焦
focusInput: () => inputRef.current?.focus(),
// 返回值
getValue: () => inputRef.current?.value,
};
});
return <input ref={inputRef} />;
});
export default function App() {
const childRef = useRef<any>(null);
return (
<>
<Child ref={childRef} />
<button onClick={() => {
childRef.current?.focusInput();
console.log("值等于", childRef.current?.getValue());
}}>子组件聚焦</button>
</>
);
}
4. useEffect
useEffect 弥补函数式组件没有生命周期的缺陷。接收2个参数:
第1个参数是个函数,函数里支持返回一个函数,它会在销毁时执行,类似于 componentWillUnmount。
第2个参数是个依赖数组,依赖项需要是能够触发组件更新的变量,比如 state 或者 props:
- 数组有值:当数组里的依赖项发生改变时,触发第一个函数参数。
- 传空数组:只会在组件 首次挂载和卸载 时,触发第一个函数参数,相当于
componentDidMount。 - 参数不传:组件 挂载或更新 时,触发第一个函数参数。相当于
componentDidMount和componentDidUpdate。
useEffect(() => {
console.log("初始化定时器");
const timer = setInterval(() => console.log("定时器"), 1000);
return () => {
// 类似于componentWillUnmount
console.log("清除定时器");
clearInterval(timer);
};
}, []);
日常使用中建议传入第二个参数,明确副作用执行的依赖项,以便减少不必要的性能消耗。
5. useLayoutEffect
同样接收2个参数:第1个参数是函数,第2个参数是依赖数组。相比 useEffect 有两个不同之处:
useLayoutEffect是 同步执行,useEffect是 异步执行。useLayoutEffect执行时机是 在DOM更新之后,浏览器绘制之前,useEffect是在 绘制之后。
useInsertionEffect的执行时机是在DOM更新之前
useLayoutEffect(() => {
console.log(divRef.current?.getBoundingClientRect());
}, []);
6. useCallback
同样接收2个参数:第1个参数是函数,第2个参数是依赖数组。返回值是一个函数,执行该函数可以得到回调函数里返回的值。如果依赖项没有发生改变,回调函数会被缓存。
import { useCallback, useState } from "react";
export default function App() {
const [count, setCount] = useState(0);
const getValue = useCallback(() => {
return count + 1;
}, [count]); //依赖项为[]时,弹窗会一直显示值:1
return (
<>
<div>{count}</div>
<button onClick={() => setCount(count + 1)}>修改</button>
<button onClick={() => alert(getValue())}>获取值</button>
</>
);
}
useCallBack 的另一个主要作用是配合 memo 缓存子组件,当传递给子组件的props有函数的时候,需要以下2处包装:
import { useState, memo, useCallback } from "react";
export default function RefundOrder() {
console.log("渲染父组件");
const [count, setCount] = useState(0);
// 1.useCallback包装函数
const getMessage = useCallback((msg: string) => console.log(msg), []);
return (
<>
<div>{count}</div>
<button onClick={() => setCount(count + 1)}>按钮</button>
<Child getMessage={getMessage} />
</>
);
}
// 2.子组件使用memo包装
const Child = memo((props: { getMessage: (msg: string) => void }) => {
console.log("渲染子组件");
return <button onClick={() => props.getMessage("hello")}>打印msg</button>;
});
7. useMemo
同样接收2个参数:第1个参数是函数,第2个参数是依赖数组。
useMemo与useCallback类似,不同之处在于前者缓存的是函数的运行结果,类似vue的computedValue。- 当
props是对象、数组的情况,这时候可以使用useMemo配合memo缓存组件。
import { useState, memo, useMemo } from "react";
const List = memo(function (props: { list: Record<string, any>[] }) {
console.log("子组件重新渲染");
return (
<>
{props.list.map((item) => (
<div key={item.id}>{item.content}</div>
))}
</>
);
});
export default function App() {
const [title, setTitle] = useState("父组件");
const [todoList, setTodoList] = useState([
{ id: 1, content: "吃饭", isDone: true },
{ id: 2, content: "睡觉", isDone: false },
{ id: 3, content: "洗澡", isDone: true },
{ id: 4, content: "刷牙", isDone: false },
]);
const changeTitle = () => {
setTitle("父组件" + Math.random().toFixed(2));
};
// useMemo 配合 memo, 实现组件缓存
const list = useMemo(
() => todoList.filter((item) => item.isDone),
[todoList]
);
return (
<>
<h1 onClick={changeTitle}>{title}</h1>
<List list={list} />
</>
);
}
8. useContext
useContext 用于爷孙组件传值,会从使用它的组件向上查找最近的Provider。
/* 父级组件App.tsx */
import { createContext } from "react";
import Grandson from "./Grandson";
// 创建上下文
export const AppContext = createContext<any>(null);
export default function App() {
const data = { name: "张三", age: 32 };
return (
<AppContext.Provider value={data}>
<Grandson />
</AppContext.Provider>
);
}
/* 孙级组件Grandson.tsx */
import { AppContext } from "./App";
import { useContext } from "react";
export default function Grandson() {
const data = useContext(AppContext);
console.log("data", data);
return <div>{data.name}</div>;
}
9. useReducer
useReducer 提供了类似redux的功能,它可以像useState一样让我们轻松的管理状态。
import { useReducer } from "react";
export default function App() {
const reducer = (state: string, action: Record<string, any>) => {
switch (action.type) {
case "add":
return state + action.payload;
case "clear":
return "";
default:
return state;
}
};
const [state, dispatch] = useReducer(
reducer,
"李四", //默认值
(name) => "hello, " + name //覆盖默认值
);
return (
<>
<div>{state}</div>
<button onClick={() => dispatch({ type: "add", payload: "a" })}>
加
</button>
<button onClick={() => dispatch({ type: "clear" })}>清除</button>
</>
);
}
感谢
感谢 React内置hooks一览 这篇文章的作者,这里的大部分内容都是从那篇文章搬过来的。
但是写这篇文章真心不是抄袭,目的仅仅是为了加深印象做个笔记,把所有代码重新手撸了一遍。