你可能不需要 Redux:使用 React Context + useReducer hook
我想修改这一点:除非你遇到 vanilla React 的问题,否则不要使用 Redux。—— Dan Abramov
Dan 早在 2016 年就说过,现在我们有了 React Context 和 useReducer hook,redux 的用例就非常少了。在这篇文章中,我们将使用 Context 和 useReducer hook 创建一个经典的待办事项列表示例。
首先,让我们设置初始状态和操作。让我们的待办事项应用包含三个操作:添加、删除和切换完成状态。
const initialState = {
todoList: []
};
const actions = {
ADD_TODO_ITEM: "ADD_TODO_ITEM",
REMOVE_TODO_ITEM: "REMOVE_TODO_ITEM",
TOGGLE_COMPLETED: "TOGGLE_COMPLETED"
};
现在让我们添加一个 reducer 函数来处理我们的操作。
const reducer = (state, action) => {
switch (action.type) {
case actions.ADD_TODO_ITEM:
return {
todoList: [
...state.todoList,
{
id: new Date().valueOf(),
label: action.todoItemLabel,
completed: false
}
]
};
case actions.REMOVE_TODO_ITEM: {
const filteredTodoItem = state.todoList.filter(
(todoItem) => todoItem.id !== action.todoItemId
);
return { todoList: filteredTodoItem };
}
case actions.TOGGLE_COMPLETED: {
const updatedTodoList = state.todoList.map((todoItem) =>
todoItem.id === action.todoItemId
? { ...todoItem, completed: !todoItem.completed }
: todoItem
);
return { todoList: updatedTodoList };
}
default:
return state;
}
};
让我们来分析一下。
- 在操作中,我扩展了现有列表,并使用(唯一)、(用户输入的值)和标志
ADD_TODO_ITEM
向列表中添加了新的待办事项。id
label
completed
- 在
REMOVE_TODO_ITEM
操作中,我根据 ID 过滤掉需要删除的待办事项。 - 在
TOGGLE_COMPLETED
操作中,我循环遍历所有待办事项并根据 ID 切换已完成标志。
现在,让我们将它们与 Context 和 useReducer 连接起来。让我们创建一个TodoListContext
。
const TodoListContext = React.createContext();
让我们创建一个Provider
返回我们TodoListContext
的提供程序的函数。
const Provider = ({ children }) => {
const [state, dispatch] = React.useReducer(reducer, initialState);
const value = {
todoList: state.todoList,
addTodoItem: (todoItemLabel) => {
dispatch({ type: actions.ADD_TODO_ITEM, todoItemLabel });
},
removeTodoItem: (todoItemId) => {
dispatch({ type: actions.REMOVE_TODO_ITEM, todoItemId });
},
markAsCompleted: (todoItemId) => {
dispatch({ type: actions.TOGGLE_COMPLETED, todoItemId });
}
};
return (
<TodoListContext.Provider value={value}>
{children}
</TodoListContext.Provider>
);
};
让我们来分析一下。
- 我们将
reducer
函数和我们的传递initialState
给 useReducer hook。这将返回状态和调度。状态将包含我们的 initialState,调度用于触发我们的操作,就像在 Redux 中一样。 - 在值对象中,我们有 todoList 状态,以及三个函数
addTodoItem
、removeTodoItem
、 和,markAsCompleted
它们分别触发ADD_TODO_ITEM
、REMOVE_TODO_ITEM
、 和TOGGLE_COMPLETED
动作。 - 我们将值对象作为 prop 传递给 的
TodoListContext
提供程序,以便我们可以使用 访问它useContext
。
太好了,现在我们的全局 store 和 reducer 都设置好了。现在让我们创建两个组件AddTodo
,TodoList
它们将使用我们的 store。
const AddTodo = () => {
const [inputValue, setInputValue] = React.useState("");
const { addTodoItem } = React.useContext(TodoListContext);
return (
<>
<input
type="text"
value={inputValue}
placeholder={"Type and add todo item"}
onChange={(e) => setInputValue(e.target.value)}
/>
<button
onClick={() => {
addTodoItem(inputValue);
setInputValue("");
}}
>
Add
</button>
</>
);
};
在 中AddTodo
,我们使用 useContext 订阅我们的TodoListContext
dispatchaddTodoItem
函数。该组件有一个输入字段,用户可以在其中输入待办事项,还有一个add
按钮,用于将待办事项添加到列表中。
const TodoList = () => {
const { todoList, removeTodoItem, markAsCompleted } = React.useContext(
TodoListContext
);
return (
<ul>
{todoList.map((todoItem) => (
<li
className={todoItem.completed ? "completed" : ""}
key={todoItem.id}
onClick={() => markAsCompleted(todoItem.id)}
>
{todoItem.label}
<button
className="delete"
onClick={() => removeTodoItem(todoItem.id)}
>
X
</button>
</li>
))}
</ul>
);
};
在TodoList
组件中,我们使用 useContext 来订阅TodoListContext
并获取todoList
状态、removeTodoItem
以及andmarkAsCompleted
dispatch 函数。我们通过 进行映射todoList
,渲染待办事项以及其旁边的 remove(X) 按钮。点击某个事项时,我们会将其标记为 ;complete
点击X
按钮时,我们会将其从列表中移除。
最后,让我们用我们的提供程序包装我们的两个组件。
export default function App() {
return (
<Provider>
<AddTodo />
<TodoList />
</Provider>
);
}
太棒了!我们使用了 Context 和 useReducer hook 来管理状态,作为 redux 的替代方案。你可以在codesandbox查看实际代码。
好了,各位,感谢阅读这篇博文。希望它对你有所帮助。欢迎留言提出你的问题和建议。
鏂囩珷鏉ユ簮锛�https://dev.to/nikhilkumaran/you-probously-don-t-need-redux-use-react-context-usereducer-hook-55ej