对于复杂的state操作逻辑,嵌套的state对象,推荐使用useReducer
使用
const [state, dispatch] = useReducer(reducer, initState);
userReducer接收两个参数,一个是reducer函数,第二个是初始化state。
useReducer返回值为最新的state和dispath,state是返回返回状态中的值,而dispatch是一个可以发布时间来更新state的方法。
reducer接受两个参数,一个是state另一个是action。
reducer是一个利用action提供的信息,将state从A转换到B的一个纯函数,具有以下几个特点
- 语法:(state, action) => newState
- Immutable:每次都返回一个newState,永远不要直接修改state对象
- Action:一个常规的Action对象通常有type和payload(可选)组成
- type:本次操作的类型,也是reducer条件判断的依据
- payload:提供操作附带的数据信息
简单使用例子:
function UseReducerDemo () {
// 定义初始的state
const initState = { count: 0 }
// 第一个参数reducer
function reducer (state, action) {
switch (action.type) {
case 'add':
return { count: state.count + 1 };
case 'sub':
return { count: state.count - 1 };
default:
throw new Error()
}
}
// 返回新的state
const [state, dispatch] = useReducer(reducer, initState)
return (
<div>
<div>count: {state.count}</div>
<button onClick={() => dispatch({ type: 'add' })}>add</button>
<button onClick={() => dispatch({ type: 'sub' })}>sub</button>
</div>
);
}
一个简单的例子对比useState和useReducer:
useState:
import React, { useState } from 'react';
import submitInfo from './api';
function UseStateDemo () {
const [name, setName] = useState('')
const [sex, setSex] = useState(0)
const [age, setAge] = useState(0)
const [avatar, setAvatar] = useState('')
const [interests, setInterests] = useState([])
const [loading, setLoading] = useState(false)
const [error, setError] = useState('')
const submit = (event) => {
event.preventDefault();
setError('');
setLoading(true);
const param = {
name,
sex,
age,
avatar,
interests
}
submitInfo({ param })
.then(() => {
setLoading(false);
})
.catch((error) => {
setError(error.message);
setName('');
setSex(0);
setAge(0);
setAvatar('');
setInterests([])
setLoading(false);
});
}
const reset = () => {
setName('');
setSex(0);
setAge(0);
setAvatar('');
setInterests([])
setLoading(false);
}
return (
<div>
{/* jsx demo... */}
<button onClick={() => submit()}></button>
<button onClick={() => reset()}></button>
</div>
);
}
export default UseStateDemo;
useReducer:
import React, { useReducer } from 'react';
import submitInfo from './api'
function UseReducerDemo () {
const initialState = {
name: '',
sex: 0,
age: 0,
avatar: '',
interests: [],
loading: false,
error: ''
}
const reducer = (state, action) => {
switch (action.type) {
case 'submit':
return { ...state, loading: false };
case 'reset':
return {
...state,
name: '',
sex: 0,
age: 0,
avatar: '',
interests: []
};
default:
return state
}
}
const [state, dispatch] = useReducer(reducer, initialState)
const submit = (event) => {
event.preventDefault();
const {
name,
sex,
age,
avatar,
interests
} = state
const param = {
name,
sex,
age,
avatar,
interests
}
submitInfo({ param })
.then(() => {
dispatch('submit')
})
.catch((error) => {
dispatch('reset')
});
}
return (
<div>
{/* jsx demo... */}
<button onClick={() => submit()}></button>
<button onClick={() => dispatch('reset')}></button>
</div>
);
}
export default UseReducerDemo;
相比之下,useReducer使代码更具有可读性,state更新逻辑更清晰。 适合的使用场景:
- 如果state是一个数组或者对象
- 如果state变化很复杂,经常一个操作需要修改很多state
- 如果你希望构建自动化测试用例来保证程序的稳定性
- 如果你需要在深层子组件里面去修改一些状态(关于这点我们下篇文章会详细介绍)
- 如果你用应用程序比较大,希望UI和业务能够分开维护