Skip to main content

useReducer

useState()用于管理简单状态。对于复杂的状态管理,可以使用useReducer() hook。它为需要多个状态操作的状态提供了更好的支持。

假设需要编写一个最喜欢的电影列表。用户可以添加电影,也可以删除已有的电影,实现方式大致如下:

import React, { useState } from 'react';

function FavoriteMovies() {
const [movies, setMovies] = useState([{ name: 'Heat' }]);

const add = movie => setMovies([...movies, movie]);

const remove = index => {
setMovies([
...movies.slice(0, index),
...movies.slice(index + 1)
]);
}

return (
// Use add(movie) and remove(index)...
);
}

添加和删除自己喜欢的电影。

状态列表需要几个操作:添加和删除电影,状态管理细节使组件混乱。

更好的解决方案是将复杂的状态管理提取到reducer中:

import React, { useReducer } from 'react';

function reducer(state, action) {
switch (action.type) {
case 'add':
return [...state, action.item];
case 'remove':
return [
...state.slice(0, action.index),
...state.slice(action.index + 1)
];
default:
throw new Error();
}
}

function FavoriteMovies() {
const [state, dispatch] = useReducer(reducer, [{ name: 'Heat' }]);

return (
// Use dispatch({ type: 'add', item: movie })
// and dispatch({ type: 'remove', index })...
);
}

reducer管理电影的状态,有两种操作类型:

  • "add"将新电影插入列表
  • "remove"从列表中按索引删除电影

打开浏览器,测试后发现组件的功能没有改变。但是这个版本的<FavoriteMovies>更容易理解,因为状态管理已经被提取到reducer中。

还有一个好处:可以将reducer 提取到一个单独的模块中,并在其他组件中重用它。

另外,即使没有组件,也可以对reducer 进行单元测试。 这就是关注点分离的威力:组件渲染UI并响应事件,而reducer 执行状态操作。