跳转到内容

Hooks

数据驱动

注意

所有的 hooks 都需要在顶层调用!!!

useState 响应式

tsx
function ChildWithState() {
	const [count, setCount] = useState(0); // 这个组件始终在顶层调用 useState
	return <button onClick={() => setCount(count + 1)}>{count}</button>;
}

注意

setXxx 是异步代码,且会触发组件重新渲染

为什么是异步? 因为它有可能被多次调用,保存在队列中,只取最后一次值渲染。优化性能

1. 操作数组

需要将数组视为只读的,不可以直接修改原数组。

  • 添加元素: [...arr, newItem]
  • 删除元素: arr.filter(item => item !== deleteItem)
  • 修改元素: arr.map(item => item === modifyItem ? newItem : item)
  • 排序元素: 先将数组复制一份

2. 对象初始化增强

可以再声明时对对象进行一些初始化逻辑操作

tsx
// 初始化声明
let [obj, setObj] = useState(() => {
	const date = new Date();
	const birthday = `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}}`;
	return {
		birthday,
		name: '张三',
		age: 18,
	};
});

// 如何响应式修改某个属性
setObj({
	...obj,
	name: '李四',
});
// 或者
setObj(Object.assign({}, obj, { name: '王五' }));

3. 进行累加

tsx
const [count, setCount] = useState(0);
// 错误
const handleClick = () => {
	setCount(count + 1);
	setCount(count + 1);
	setCount(count + 1);
	console.log(count); // 输出结果为0,因为异步并且读取最后一次的值
};

// 正确
const handleClick = () => {
	setCount((prev) => prev + 1);
	setCount((prev) => prev + 1);
	setCount((prev) => prev + 1);
	console.log(count); // 输出结果为3,取每次上一次结果进行累加
};

useReducer 状态机

使用场景: 复杂表单的操作按钮处理不同逻辑

tsx
/**
 * 状态机
 * @param reducer - 状态机,处理函数,调用dispatch执行
 * @param initialState - 默认值
 * @param init - 可选,初始化函数修饰默认值,只执行一次
 */
const [state, dispatch] = useReducer(reducer, initialState,init?);

使用

tsx
const initState = {
	count: 0,
};

type State = typeof initState;

// 一开始不执行
const reducer = (state: State, action: { type: string }) => {
	switch (action.type) {
		case 'add':
			return {
				count: state.count + 1,
			};
		default:
			return state;
	}
};

const initFn = (state: State) => {
	return {
		count: Math.abs(state.count),
	};
};

function App() {
	const [state, dispatch] = useReducer(reducer, initState, initFn);

	return (
		<>
			<div className="card">
				<button onClick={() => dispatch({ type: 'add' })}>count is {state.count}</button>
			</div>
		</>
	);
}
export default App;

useImmer 直接修改state

React 的一个第三方 Hook,用于管理 state;使用场景: 复杂对象、深层嵌套、频繁改动数组/对象时,非常省代码

安装

shell
npm install immer use-immer

使用

  1. 使用 useState 方式
tsx
import { useState } from 'react';

function App() {
	const [person, setPerson] = useState({
		name: 'Jane Doe',
		age: 42,
		hobby: {
			name: 'Coding',
			level: 'Advanced',
		},
	});
	return (
		<>
			<div>姓名: {person.name}</div>
			<div>展示爱好: {person.hobby.name}</div>
			<button
				onClick={() => {
					// ⚠️ 非常复杂
					setPerson({
						...person,
						hobby: {
							...person.hobby,
							name: 'Cooking', // 修改值一定要放后面进行覆盖!
						},
					});
				}}
			>
				修改爱好
			</button>
		</>
	);
}

export default App;
  1. 使用 useImmer 方式
tsx
import { useImmer } from 'use-immer';

function App() {
	const [person, setPerson] = useImmer({
		name: 'Jane Doe',
		age: 42,
		hobby: {
			name: 'Coding',
			level: 'Advanced',
		},
	});
	return (
		<>
			<div>姓名: {person.name}</div>
			<div>展示爱好: {person.hobby.name}</div>
			<button
				onClick={() => {
					setPerson((draft) => {
						draft.hobby.name = 'Cooking';
					});
				}}
			>
				修改爱好
			</button>
		</>
	);
}

export default App;

// 数组形式
setPerson((draft) => {
	draft.push(); // 直接使用api
});

// 普通类型
setPerson(true);
  1. useImmerReducer

在 dispatch 的 action 中直接修改 state 的值,不需要在返回新的[...state]

Will Try My Best.