React Hooks中的useState是功能强大的一个hook,它允许我们在不使用class组件的情况下实现组件内部state的管理。但是如果没有正确和优雅的使用useState,也很容易造成代码混乱和难以维护。
1. 用多个state而不是一个复杂的state对象
刚开始使用useState时,我们可能倾向于只定义一个state对象来存储所有数据:
const [state, setState] = useState({
name: '',
age: 0,
address: ''
});
这样的问题在于,每次更新任何一个字段时,我们都需要先拷贝一份完整的state对象,修改对应字段,然后再调用setState。代码会变得冗余且难以维护。
相比之下,拆分成多个独立的state会更加简单明了:
const [name, setName] = useState('');
const [age, setAge] = useState(0);
const [address, setAddress] = useState('');
只需要更新相关数据即可。但这种使用方法也只适用于组件体积小,需要定义的state较少时使用,当我们的组件较为复杂,需要定义的state较多时,上述方法就会导致下面的一系列问题:
- 组件状态难以管理:多个 useState 意味着需要管理多个独立的状态与设置状态的函数,状态之间的关系容易混乱,导致难以管理。
- 性能问题: 使用多个 useState 会增加渲染次数,可能会对性能造成影响。每次状态改变都会引起组件的重新渲染。
- 冗余代码:多个 useState 需要定义多个状态变量和设置状态的函数,导致冗余代码增多。
- 逻辑分散:大量 useState 钩子分散在组件各处,导致相关状态逻辑也分散,不利于管理和理解。
- 增加复杂性:组件变得更复杂,可读性和可维护性下降。
2. 封装自定义hooks复用逻辑
如果单个组件较为复杂,组件内部定义state较多时,我们可以通过封装如下自定义hooks来解决。
import { useMemoizedFn, useSafeState } from 'ahooks';
import { isFunction, isObjectLike } from 'lodash-es';
import React, { useMemo } from 'react';
function useSimpleState<T>(s: T) {
const [state, setState] = useSafeState(s);
const stateChange = useMemoizedFn((d: Partial<T>) => setState((old) => ({ ...old, ...d })));
const isObject = useMemo(() => isObjectLike(s) || (isFunction(s) && isObjectLike(s())), []);
const result = useMemo(
() => [state, isObject ? stateChange : setState, setState] as [T, (data: Partial<T>) => void, React.Dispatch<React.SetStateAction<T>>],
[isObject, setState, state, stateChange],
);
return result;
}
export default useSimpleState;
以上自定义hook中使用到的ahooks和lodash的方法,可以到ahooks和lodash的官方文档去了解,这里就不作过多的赘述。 我们在定义state时就可以使用当前封装的自定义hook useSimpleState。
function ComponentA() {
const [state, updateState] = useSimpleState({name:string, age:number, sex:string});
//...
//修改状态
updateState({name:'小明'})
}
在多个组件中如果存在重复的业务逻辑,我们可以通过自定义hooks将其抽象成可重用的逻辑单元。
例如可以定一个useProfile hook来初始化和管理用户配置文件:
import {useState} from 'react';
function useProfile() {
const [name, setName] = useState('');
const [age, setAge] = useState(0);
const [avatar, setAvatar] = useState('');
// 省略业务逻辑...
return {
name,
age,
avatar,
setName,
setAge,
setAvatar
};
}
export default useProfile;
不同组件可以轻松复用这个逻辑:
function ComponentA() {
const {name, age, setName, setAge} = useProfile();
//...
}
function ComponentB() {
const {avatar, setAvatar} = useProfile();
//...
}
这种方式提高了代码的抽象级别,也使测试和维护更为方便。
3. useReducer优化复杂逻辑
当业务逻辑较为复杂时,可以使用useReducer hook代替多个useState。useReducer允许我们通过reducer函数集中管理组件状态逻辑。
import {useReducer} from 'react';
const initialState = {
name: '',
age: 0,
friends: []
};
const reducer = (state, action) => {
switch(action.type) {
case 'SET_NAME':
return {...state, name: action.payload};
case 'SET_AGE':
return {...state, age: action.payload};
case 'ADD_FRIEND':
return {...state, friends: [...state.friends, action.payload]};
// 省略其他action
}
}
function Component() {
const [state, dispatch] = useReducer(reducer, initialState);
const setName = (name) => {
dispatch({type: 'SET_NAME', payload: name});
};
// 其他action creator函数
return (
<div>
{ /* 使用state和action creators */ }
</div>
)
}
这种方式可以避免定义多个useState同时又保持代码解耦,很好的管理了组件内的状态逻辑复杂性。
结语: useState 是 React 中非常强大且灵活的状态管理工具。通过合理地使用 useState,我们可以更加优雅地管理组件的状态,并提升代码的可维护性和可读性。希望这篇文章可以帮助大家更好地理解和使用 useState。