引言
在前面的文章小编也是花了大量时间来学习React当中的路由,差点走火入魔哈,好在最近读了一本书《走向光明》。有感兴趣的小伙伴可以去看看,对自己最近的专注学习有着相当大的提升,也是这本书籍,让小编今天来攻克React当中的难点,如何理解Reducer和Context的结合使用。接下来这篇文章耗费小编一个月功力完成,让我们赶快进入Reducer结合Context的学习吧!!!
useReducer的核心
useReducer 是 React 提供的状态管理 Hook,适用于复杂状态逻辑或包含多个子值的状态对象。它通过 reducer 函数(接收 state 和 action,返回新状态)来更新状态,逻辑集中且可预测。
useContext的核心
useContext是 React 提供的 Hook,用于跨层级共享状态。通过Context.Provide向组件树传递数据,子组件无需逐层传递 props 即可访问共享状态。
useContext 和 useReducer(结合开发管理)
redux的前身,跨层级全局应用将 useReducer 与 useContext 结合,可以构建一个轻量级全局状态管理方案,常用于替代 Redux 的简单场景。
创建两个组件结合开发项目
1.全局创建TodoContext上下文对象
import {
createContext,
} from 'react'
// 创建了上下文
export const TodoContext = createContext(null)
在这里我们需要向全局创建全局的状态的应用管理
import{
createContext,
useContext
} from 'react';
export const TodoContext = createContext(null);
// 自定义hooks
export function useTodoContext(){
return useContext(TodoContext)
}
在这里有个非常重要的一点,封装了 useContext(TodoContext)在组件中使用时,不需要每次都导入 useContext 和 TodoContext,只需调用 useTodoContext()。
| 用法 | 原始写法 | 封装后写法 |
|---|---|---|
| 引入依赖 | import { useContext } + import { TodoContext } | 只需 import { useTodoContext } |
| 使用方式 | useContext(TodoContext) | useTodoContext() |
| 可维护性 | 较低 | 更高 |
| 可读性 | 一般 | 更清晰 |
| 错误提示 | 需手动检查 | 可封装检查逻辑 |
在这里便需要将自己的hooks封装到src下的hooks目录当中
import{
useContext
} from 'react'
import{
TodoContext
}
export function useTodoContext(){
return useContext(TodoContext)
}
全局状态当中的代码就应该将封装的函数部分删去
import {
createContext,
} from 'react'
// 上下文
export const TodoContext = createContext(null)
2.全局获取数据
import { useState } from 'react'
import './App.css'
import {
TodoContext
} from '@/TodoContext'
function App() {
// const todosHook = useTodos()
return (
// App 状态管理
<TodoContext.Provider value="">
</TodoContext.Provider>
)
}
export default App
在这边使用主函数当中的TodoContext函数为子组件当中的全局上下文当中传递数据,而useTodoContext则为在TodoContext.Provider的子组件当中使用提供的全局数据。在浏览器当中可以查看此时的组件树数据:
3.创建reducers
当我们自定义Hook的时候,实现了组件和状态的分离,再使用
useContext可以使定义的Hooks里面的状态和方法在任何组件层级当中可以使用。如果需要更大规模的使用这个Hooks,即应用管理和状态管理好,我们就需要使用到useReducer来使用对它负责useTodos。
// 重要作用负责状态的正确,全局复用
function todoReducer(state,action){
switch(action.type){
case 'ADD_TODO':
return [...state, {
id: Date.now(),
text: action.text,
done: false
}]
case 'TOGGLE_TODO':
return state.map(todo=>
todo.id === action.id ? {
...todo,
done: !todo.done
} : todo
)
case 'REMOVE_TODO':
return state.filter(todo=>todo.id !== action.id)
default:
return state
}
}
export default todoReducer
4.创建useTodos
import {
useReducer,
} from 'react'
import todoReducer from '@/reducers/todoReducer'
// 参数的默认值
// 解构 []=[] {} = {}
export function useTodos(initial = []) {
return {
todos,
addTodo,
toggleTodo,
removeTodo
}
}
返回最初的方法,原始的配方,但是这里用料变了。在这里需要使用到useReducer方法,现在需要对下面的方法当中使用函数来调用。
const [todos, dispatch] = useReducer(todoReducer,initial)
initial = []是什么?
1. initial
- 这是一个参数名,表示“初始值”。
- 调用这个 Hook 时可以传入一个初始的 todo 列表,比如:
深色版本
useTodos([
{ id: 1, text: '学习React', completed: false }
])
2. = []
- 这是 默认参数值(default parameter value) 。
- 如果调用时没有传入参数,
initial就会默认是一个空数组[]。 - 这样即使没有提供初始值,代码也不会出错。
5.Hooks引入
将封装好的useTodos引入根组件当中,并向value当中提供tooksHook让我们看到打印的组件效果。
import { useState } from 'react'
import './App.css'
import {
TodoContext
} from '@/TodoContext'
import { useTodos } from './hooks/useTodos'
import AddTodo from './components/AddTodo'
import TodoList from './components/TodoList'
function App() {
const todosHook = useTodos()
return (
// App 状态管理
<TodoContext.Provider value={todosHook}>
<h1>Todo List</h1>
<AddTodo />
<TodoList />
</TodoContext.Provider>
)
}
export default App
可以看到在TodoContext当中的value有了todosHook当中的函数方法。
6.AddTodo中的难点
import {
useState // 私有
} from 'react'
import { useTodoContext } from '@/hooks/useTodoContext'
const AddTodo = () => {
const [text, setText] = useState('')
const { addTodo } = useTodoContext() // 跨层级
const handleSubmit = (e) => {
e.preventDefault()
// 全局管理
if (text.trim()) {
addTodo(text.trim())
setText('')
}
}
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={text}
onChange={e => setText(e.target.value)}
/>
<button type="submit">Add</button>
</form>
)
}
export default AddTodo
如何理解这段代码当中的下面代码是这段代码当中的关键问题!!!
import { useTodoContext } from '@/hooks/useTodoContext'
const { addTodo } = useTodoContext() // 跨层级
理解了这段代码就是理解useContext的关键问题,在根组件当中的value当中我们向全局提供了可以拿到的value数据,当我们需要在其他组件当中用到该数据时将其组件引入。 而第二段代码当中我们需要从根组件当中自己所拿到的value值当中从中解构出咱们所需要的值,并且定义表单提交时的处理逻辑,实现添加Todo的功能。
7.TodoList简单
import {
useTodoContext
} from '@/hooks/useTodoContext'
const TodoList = () => {
const {
todos,
toggleTodo,
removeTodo
} = useTodoContext()
if (todos.length === 0) {
return <p>No todos</p>
}
return (
<ul>
{
todos.map(todo => (
<li key={todo.id}>
<span
onClick={() => toggleTodo(todo.id)}
style={{ textDecoration: todo.done ? 'line-through' : 'none' }}
>
{todo.text}
</span>
<button onClick={() => removeTodo(todo.id)}>Remove</button>
</li>
))
}
</ul>
)
}
export default TodoList
在这里就可以实现跨组件拿到数据并对数据进行修改,可以发现数据的内容全都是响应式的。通过上述useReducer和useContext的实际案例的学习可以让我们更快地掌握它两结合使用的技巧。如果掌握好了React当中的这两大Hooks,可以发现写起项目来将会得心应手。
总结
- 在写项目当中为了方便代码的简洁和复用性,需要在全局创建
TodoContext上下文对象,并且封装好Hooks函数使其可以在全局当中进行引用,方便代码的复用性和简洁性 - 将我们的根组件当中引用
TodoContext组件并向全层级组件提供数据 - 当我们在写
useTodos方法时,需要使用到useReducer函数来对自定义Hooks进行应用管理和状态管理,然后在useTodos当中通过useReducer来调用创建的方法:
const [todos, dispatch] = useReducer(todoReducer, initValue)
- 完成上述功能完善之后,需要将封装好的
useTodos在根组件当中引用,并且在根组件当中向全层级组件提供数据 - 当我们在其他子组件当中需要获取数据的时候,使用
useContext来获取数据并且解构出我们所需要的值,写好逻辑函数完成功能 - 最后就是表单的渲染,需要将我们写好的方法拿到
TodoList表单当中使用,这样整体页面的渲染就算是完成了,就可以看到页面的简单实现效果了
如果大家对于useReducer和useContext还不熟悉的话,可以移步到小编写的另外两篇文章。
初识React状态管理:useReducer
React"隔空取物"-useContext 用起来就这么简单