Redux是什么
Redux是状态管理层的解决方案,全局状态管理中心
我们看一个简单的小例子
import { useState } from "react";
function Counter(){
// State :计数器值
const [counter,setCounter] = useState(0)
// Action:当发生某一操作时,更新状态
const increment = ()=>{
setCounter(prevCounter=>prevCounter+1)
}
// View:UI定义
return(
<div>
Value:{counter}
<button onClick={increment}>Increment</button>
</div>
);
}
复制代码
这是个简单的计数器案例
我们点击一次按钮,数值会加1
一个组件内部逻辑基本上可以分为这三个部分
Redux进行了一步抽象,将State
与Action
提取出来,让组件更专注的实现View
组件会从Redux中获取State
更新状态,组件可以调用actions去修改Redux中State
的值。这是Redux中基本的数据流的流程图
什么时候用Redux
B2想要和B3共享状态,我们可以将状态放在B1中
B2想要和A2共享状态,我们可以将状态放在A1中
这就叫做状态共享
但是如果时B2和A2这种,这就产生了很深层次的嵌套
Redux通过引入全局的状态管理的方式,任何一个组件,想要获取什么状态,直接从Redux中获取就行了,这就解决了状态一层一层传递的问题
如果组件内的状态管理逻辑比较复杂,需要分层的时候Redux也可以帮我们进行逻辑的管理
Redux术语
Store
store
就是存储全局状态的一个地方
怎么去获取store
对象呢?
我们可以通过configureStore
这个api创建store
对象
import { configureStore } from "@reduxjs/toolkit";
const store = configureStore({reducer:conterReducer})
console.log(store.getState())
复制代码
我们可以通过@reduxjs/toolkit
这个库引入configureStore
这个库其实就是基于reduxjs
做的封装,简称RTK
,通过这个库我们来使用Redux可以变得更加简洁
我们通过configureStore
可以创建一个store
对象,其中configureStore
接收一个对象,这个对象有一个叫reducer
的属性,待会我们会介绍
Action
Action
描述状态发生的变化,他会对应状态修改的一个动作
Action
其实就是一个普通的js对象
const addTodoAction = {
type:"todos/todoAdded",
payload:"Buy milk"
}
复制代码
这个Action
表明我要增加一个待办事项的这个条目,他所必须要包含的属性时type
属性,type
属性是一个字符串,这个字符串描述了Action
对应的状态变化
Action Creator
Action Creator
是一个函数用来返回一个Action
对象的
const add Todo = text =>{
return {
type:'todos/todoAdded',
payload:text
}
}
复制代码
这是一个Action Creator
,这个payload
是通过函数传进来的参数。这可以让我们更灵活的创建Action
。
Dispatch
Dispatch
:发送一个Action
store.dispatch({type:'todos/todoAdded',payload:'learn react'})
console.log(store.gatState())
复制代码
Reducer
Reducer
是一个函数,会接收发送出来的Action
,然后会根据这个Action
和当前Redux的全局Store
,进行一系列的处理,然后计算新的状态。
const initialState = { todos:[]}
function todoReducer(state=initialState,action){
if(action.type==='todos/todoAdded'){
return {
...state,
todos:[...state.todos,action.payload]
}
}
return state
}
复制代码
逻辑处理流程
这个逻辑处理流程大概分为一下几步
1.事件产生
一个UI组件会定义一些事件,例如点击按钮等
2.发送Action
当事件产生了之后,想要修改State
,但是不能够直接去修改,需要通过Dispatch
发送一个Action
给Reducer
3.Reducer处理逻辑
Reducer
接收到Action
之后,会根据Action
的type
执行对应的函数,根据Action
的payload
和当前的State
更新State
,
4.重新渲染UI组件
当State
被更新后,会重新来渲染UI组件
Redux Toolkit
RTK
是基于Redux
的上层封装库,基于常见业务场景,提供了更加简洁的API
,简化Redux
的使用复杂度。
之前没有Redux Toolkit
的时候我们需要写大量的模板代码,课程也是以Redux Toolkit
为例来讲解如何使用Redux
Redux
的使用我感觉还是优点乱的,这里以一个todolist来做一个例子吧
创建store
想要在全局使用Redux,我们需要在App
组件的上面再包裹一个父组件Provider
,这个组件是Redux提供的,需要接收store
作为属性
那么store
从何而来呢?
我们这里使用Redux Toolkit
提供的configureStore()
生成。
先新建一个文件夹名叫states
,用来存储全局state
在文件夹下创建一个store.js
的文件,这个文件向外提供全局store
store.js
import { configureStore } from '@reduxjs/toolkit';
const store = configureStore({
reducer: {
// Slice.reducer
}
})
export default store;
复制代码
后面我们会创建Redux中存储的全局的state
,创建之后需要将state
的reducer
放入reducer
中,才能在全局使用。
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { Provider } from 'react-Redux'
import store from './states/store'
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>,
document.getElementById('root')
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
复制代码
创建slice
首先说一下slice
是个什么东西,在前面我们提到了sotre
中存放的是state
与reducer
,其中reducer
中包含多个处理Action
的程序,但是对于一个项目来说,我们通常不仅仅只有一个全局state
和reducer
要用Redux维护,所以Redux为store
提供了slice
,目的是为了能够将不同模块区分开来,同一模块的state
和reducer
包装在一个slice
中,然后,再将slice
的reducer
传给store
。
这里我也有一点不太明白,明明store
中需要state
与reducer
,为什么在configureStore
的时候,不是传入整个slice
,而是单独传入reducer
呢?问题先放下,我们就先记住,继续往下看,有明白的大佬,能否帮忙解答一下?
创建slice
我们要利用createSlice
这个api,这是'@reduxjs/toolkit'
提供的api,接收一个name
,一个initialState
,作为初始的state
,reducers
,包含处理Action
的各种方法
todoListSlice.js
import { createSlice } from '@reduxjs/toolkit'
const initialState = []
const todoListSlice = createSlice({
name: "todoList",
initialState,
reducers: {
addTodo(state, action) {
state.push(action.payload)
}
}
})
export const { addTodo } = todoListSlice.actions;
export default todoListSlice.reducer;
复制代码
创建完成,我们需要向外界暴露出一个是addTodo
的Action Creator
,用来创建Action
的,这个addTodo
虽然和reducers
中的函数名一样,但是并不是同一个意思,我第一次在这里也糊涂了。
先说一下createSlice
返回的对象
这里面的Actions
中的addTodo
其实是一个actionCreator
,用来产生Action
的
而我们定义在reducers
中的addTodo
方法才是真正的reducer
,用来执行state
的更新的方法。
既然我们已经创建了slice
了,那么我们也需要将slice
的reducer
放进store
的reducer
才行,这样才能在全局使用这个slice
更新store.js
import { configureStore } from '@reduxjs/toolkit';
import todoList from './todoListSlice';
const store = configureStore({
reducer: {
todoList
}
})
export default store;
复制代码
使用state
到目前为止,我们相当于已经完成了store
部分的工作
下面就是怎么使用state
,想要使用state
,我们需要用到react-redux
提供的useSelector()
这个hook方法。
使用示例
App.js
import './App.css';
import { useState } from 'react';
import { useSelector } from 'react-redux';
function App() {
const [todoText, setTodoText] = useState("")
const todoList = useSelector(state => state.todoList)
const onInputChange = (event) => {
}
const onAddButtonClick = (event) => {
}
return (
<div className="App">
<div>
<input type="text" onChange={onInputChange} />
<button onClick={onAddButtonClick}>添加</button>
</div>
<div>
{
todoList.map(todoItem => {
return (
<div key={todoItem.id}>
{todoItem.text}
</div>
)
})
}
</div>
</div>
);
}
export default App;
复制代码
如果我们将state
的初始值设置为
const initialState = [
{
id:0,
text:"Hello"
},
{
id:1,
text:"world"
}
]
复制代码
那么页面就会显示这样的效果了
更新state
上面我们已经实现了在组件中使用state
那我们怎么实现更新state
呢?
更新state
需要在EventHandle
中调用Dispatch
,Dispatch
需要通过react-redux
提供的 useDispatch
这个hook方法获取,
Dispatch
会发送一个Action
,这个Action
需要我们使用ActionCreator
创建,就是刚刚我们在slice
中export的ActionCreator
方法
最终实现代码:
import './App.css';
import { useState } from 'react';
import { useDispatch,useSelector } from 'react-Redux';
import { nanoid } from '@reduxjs/toolkit';
import { addTodo } from './states/todoListSlice'
function App() {
const [todoText, setTodoText] = useState("")
const todoList = useSelector(state => state.todoList)
const dispatch = useDispatch();
const onInputChange = (event) => {
setTodoText(event.target.value)
}
const onAddButtonClick = (event) => {
// 提交
dispatch(
addTodo({
text: todoText,
id: nanoid()
}),
);
}
return (
<div className="App">
<div>
<input type="text" onChange={onInputChange} />
<button onClick={onAddButtonClick}>添加</button>
</div>
<div>
{
todoList.map(todoItem => {
return (
<div key={todoItem.id}>
{todoItem.text}
</div>
)
})
}
</div>
</div>
);
}
export default App;
复制代码
最终实现效果:
到目前为止,就已经把Redux加Redux Toolkit的使用流程讲完了。
异步数据流
但是实际项目中我们会遇到的问题是,当我们点击动作后,需要向服务端发起数据获取请求,当数据请求到了之后,再进行reducer
中修改数据,那这样我们怎么处理呢?
这就是Redux的异步数据流
在Redux toolkit中为我们提供了createAsyncThunk()
的api,这个相当于一个中间件。
接收到Event Handler
的Dispatch
之后,并不会立即将Dispatch
发送传给reducer
,而是被中间件拦截,中间件会再向Reducer
发送Action
.
对于createAsyncThunk()
这个api在接收到Action
时会给reducer
发送一个pending
的Action
,异步结果返回时如果有错误,会发送一个rejected
的Action
,如果成功,会发送一个fulfilled
的Action
具体的用法可以参照这个例子。
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import { client } from '../api/client';
const initialState = {
posts: [],
status: 'idle',
error: null,
};
export const fetchPosts = createAsyncThunk('posts/fetchPosts', async () => {
const response = await client.get('/fakeApi/posts');
return response.posts;
});
export const addPost = createAsyncThunk(
'posts/addPost',
async (initialPost) => {
const response = await client.post('/fakeApi/posts', { post: initialPost });
return response.post;
},
);
const postsSlice = createSlice({
name: 'posts',
initialState,
reducers: {
// addPost(state, action) {
// state.posts.push(action.payload);
// },
},
extraReducers: {
[fetchPosts.pending]: (state, action) => {
state.status = 'loading';
},
[fetchPosts.fulfilled]: (state, action) => {
state.status = 'succeeded';
state.posts = action.payload;
},
[fetchPosts.rejected]: (state, action) => {
state.status = 'failed';
state.error = action.error.message;
},
[addPost.fulfilled]: (state, action) => {
state.status = 'idle'
},
},
});
// export const { addPost } = postsSlice.actions;
export default postsSlice.reducer;
复制代码
日记
我真的想哭了,这Redux是什么东西,太乱了吧,迟早完蛋。(流下了不学无术的泪水)
边学习,边做笔记的我一度想要放弃。
我就想要创建一个全局变量,有这么难?
我觉得Redux的这种处理逻辑没有问题,但是编写代码的时候,感觉代码的流程或者逻辑很混乱
React Redux的学习我已经脱了两天了,前两天是因为有事,但昨天真的是没学明白。从框图的逻辑上来说,我可以理解,但是代码流就感觉没什么逻辑。