主题
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使用
- 使用 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;- 使用 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);- useImmerReducer
在 dispatch 的 action 中直接修改 state 的值,不需要在返回新的[...state]