redux与todolist实践 | 青训营

97 阅读3分钟

首先分析一下一个todolist具有哪些功能:

  1. 具有一个输入框,能够添加待办事项(todoItem),每个待办事项应具有独有的id
  2. 具有一个展示待办事项的列表,能够删除待办事项,或是直接清空列表
  3. 待办事项应该具有“状态”,能够标注“已完成”或“未完成”,且具有批量实现的功能
  4. 能够加上标签,用作筛选

如何根据这些功能抽象出数据结构呢?首先最重要的是待办事项(todoItem)的列表,并附带有是否完成、标签、id等信息,这是核心数据,而另一类数据为筛选数据,提供筛选待办事项的条件,因此state的结构设计如下:

  • 待办事项位于列表中,每个选项为一个对象,具有id,completed,color,text等字段
  • 筛选条件对象有status和colors两个字段
const initState = {
	todos: [
		{id: 0, text: 'learn react', completed: true},
		{id: 1, text: 'learn vue', completed: false, color: 'green'}
	]
	filter: {
		status: 'Active',
		colors: ['red', 'blue']
	}	
}

现在我们已经有了一个原始数据,如果要声明一个reducer函数,还需要设计action

根据功能,action应包括:添加item,删除item,清空item,状态切换,全部完成,全部未完成,设置标签,删除标签,设置过滤标签选项等,这里不在一一列出了。

action设计完成后,可以创建reducer函数了

const appReducer = (state = initState, action) => {
	switch (action.type) {
		case 'todos/todoAdded': {
			return {
				...initState,
				todos: [
					...state.todos,
					{id: nextTodoId(state.todos), 
					 text: action.payload, 
					 completed: false
					}
				]
			} 
		} 
		// case: ...
		default:
			return state
	}
}

切记,不可以直接修改state里面的数据,必须通过复制得到一个副本并对副本进行修改来进行(可以使用拓展运算符),因为直接修改可能会导致错误。

因为todolist的功能很复杂,在写reducer函数的时候,为了方便维护,可以根据state的分类进行拆分,分为多个slice,然后通过combineReducers组合起来

import {combineReducers} from 'redux'

const rootReducer = combineReducers({
		todos: todosReducer, 
		filter: filtersReducer	
}) 

创建好根reducer函数后,可以创建store了,将其传入createStore()中生成store

useSelector

接下来是通过react-redux库,结合redux使用react,它提供了一个hook:useSelector ,这个hook接收一个selector函数,这个函数接收store的state作为参数,然后从state中取值并返回

import { useSelector } from 'react-redux'

const selectTodos = state => state.todos

const TodoList = () => {
	const todos = useSelector(selectorTodos)
	const renderedListItems = todos.map(todo => {
		return <TodolistItem key={todo.id} todo={todo}>	
	})
	return <ul className='todo-list'>{renderdListItems}</il>
}

useSelector会自动订阅store,当dispatch 一个 action后,store会更新,如果selector返回的值和上一次相比发生变化,而组件中的数据也会做出相应的更新,并重新渲染视图,但是要注意只要selector返回的结果是新地址引用,组件就会重新渲染,即使数据值没有改变

react组件

useDispatch

上面的操作读取了store中的数据,而下一步就是学习另一个hook,它是用来向store发送action的useDispatch hook,该hook会返回store.dispatch

import { useDispatch } from 'react-redux'

const todoText = () => {
	cosnt [text, setText] = useState('') 
	const dispatch = useDispatch()
	const handleChange = e => setText(e.target.value)
	const handleKeyDown = e => {
		const trimmedText = e.target.value.trim()
		if (e.key === 'Enter' && trimmedText ) {
			dispatch({type: 'todos/todoAdded', payload: trimmedText})
			setText('')
		}
	}
	return ...
}

而除了两个hook,我们还需要在组件外用组件进行包裹,并将store作为prop传递给组件,这样才能在被包裹的组件中访问到store中的数据

使用react-redux主要是有以下关键部分:

  • 使用 useSelector hook 函数来读取 React 组件中的数据
  • 使用 useDispatch hook 函数在组件中 dispatch action
  • 使用 <Provider store={store}> 组件包裹 <App> 组件,这样其他组件都能够和 store 进行交互

通过上述方法,完成了todolist的一个基础设计以及添加待办事项功能的实现,然而一个todolist的功能还是相当复杂的,这里的其他部分不再贴代码,建议移步redux官网学习。可以看到,redux的使用还是相当繁琐的,redux团队也开发了Redux Toolkit,提供了更为简单的方法来编写代码管理状态,后面也可以用新的方法尝试一下