摘要
本篇主要是redux讲解。
- redux
redux
Redux 是React里最常用的集中状态管理工具,类似vue里的vuex,是可以独立于框架之外运行的。
redux一般存的是全局通用的数据,这样,很多组件需要用到的数据存到这里,数据线就会非常清晰而且简单。
使用步骤
-
定义reducer函数
-
使用createStore方法传入reducer函数,生成一个store实例
-
通过store实例subcribe订阅数据变化(数据一旦变化,就可以得到通知)
-
通过store实例的dispatch函数提交action更新状态(告诉reducer 你想怎么改数据)
-
通过store实例的getState方法获取最新状态更新到视图中
步骤多,很多是一个样板代码。
redux快速体验
既然可以独立于react运行,那么我们单独使用下。不和任何框架绑定,不使用任何构建工具。使用纯redux实现计数器。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<button id="dec">-</button>
<button id="count">0</button>
<button id="add">+</button>
<script src="https://unpkg.com/browse/redux@5.0.0-alpha.0/dist/redux.js"></script>
<script>
// 1.定义reducer函数,根据不同的action对象 返回不同的新的state
// 两个参数,一个是state 管理的数据;另一个是action,其属性type标记什么情况下想要做什么样的修改
function reducer(state={count:0},action){
if(action.type == 'add'){
return {count:state.count++}//数据不可变原则,跟useState一样,不能修改,只能覆盖。
}else if(action.type == 'dec'){
return {count:state.count--}
}
return state
}
// 2. 传入reducer函数生成一个store实例
const store = Redux.createStore(reducer)
// 3. 通过store实例的subscribe方法订阅数据的变化,每次state变化会自动进入回调执行
store.subscribe(()=>{
console.log('state发生变更,值为',store.getState())
//5. 通过store.getState方法获取最新值并写入到视图
document.getElementById('count').innerText = store.getState().count
})
// 4. 通过store实例的dispatch函数提交action更改状态
const addBtn = document.querySelector('#add')
addBtn.addEventListener('click',()=>{
//++
store.dispatch({
type:'add'
})
})
const decBtn = document.querySelector('#dec')
decBtn.addEventListener('click',()=>{
//--
store.dispatch({
type:'dec'
})
})
</script>
</body>
</html>
redux管理数据流程梳理:
为了职责清晰,数据流向明确,Redux把整个数据修改的流程分成了三个核心概念,分别是:state/action/reducer
-
state: 一个对象,存放着我们管理的数据状态
-
action: 一个对象, 用来描述你想怎么改数据
-
reducer: 一个函数,根据action的描述生成一个新的state
redux 和 react 结合
redux投入到react项目中是最常用的。
在React里使用redux,官方要求额外要安装另外2个npm插件:
-
Redux Toolkit(RTK),官方推荐编写Redux逻辑的方式,是一套工具集合。可以简化redux的写法。
-
react-redux:用来连接 redux 和 组件的 中间件。
一.通过cli创建React项目环境
创建名字:test-redux 的项目
npx create-react-app test-redux
二.安装redux配套插件
npm i @reduxjs/toolkit react-redux
启动项目。
三.创建store目录
│├── store 状态机配置
││├── index.js 状态机入口
││└── modules 状态机模块
│ │ └── counterStore.js
我们依然实现一个计数器
整体路径熟悉
- redux store里的相关逻辑:
-
配置子模块
-
配置store并注册子模块
- react组件里的相关逻辑:
-
注入store[使用 react-redux做桥接]
-
使用store中的数据
-
修改store中数据
四.子模块书写
store/modules/counterStore.js
使用react toolkit 创建子store模块。
import { createSlice } form '@reduxjs/toolkit'
const counterStore = createSlice({
name:'counter',
//初始化state
initialState:{
count:0
},
//修改状态的方法, 同步方法, 支持直接修改
reducer:{
add(state){
state.count ++
},
dec(state){
state.count --
}
}
})
// 解构出来actionCreater 函数
const {add,dec} = counterStore.actions
// 获取reducer
const reducer = counterStore.reducer
// 以按需导出的方式导出actionCreater
export {add,dec}
// 以默认的方式导出reducer
export default reducer
五.状态机入口引入注册书写
store/index.js
import { configureStore} from '@reduxjs/toolkit'
import counterStore from './modules/counterStore'
// 创建根store并组合子模块
const store = configureStore({
reducer:{
// counter这个名字就是后面页面里useSelector回调里使用的store子模块
counter:counterStore
}
})
export default store
六.app主入口引入store
主要利用react-redux 的 内置 Provider 组件 来做store的全局注入。
import store form './store'
import {Provider} from 'react-redux'
const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(
<Provider store={store}>
<App/>
</Provider>
)
七. react组件里使用store数据
主要利用react-redux 的一个钩子函数useSelector。它的作用是把store中数组映射到组件中去。
app.jsx
import {useSelector} from 'react-redux'
function App(){
const { count } = useSelector(state => state.counter)//useSelector 里是一个回调函数的形式,return回store
return(
<div className="App">
{count}
</div>
)
}
八. react组件里修改store数据
主要利用react-redux 的一个钩子函数 useDispatch 。它的作用是生成提交action对象的dispatch函数。
app.jsx
import { useDispatch,useSelector } from 'react-redux'
import { add,dec} from './store/modules/counterStore'//导入创建的action方法
function App(){
const { count } = useSelector(state => state.counter)
const dispatch = useDispatch()//拿到dispatch函数
return(
<div className="App">
<button onClick={()=> dispatch(dec())}>-</button>
{count}
<button onClick={()=> dispatch(add())}>+</button>
</div>
)
}
提交action时传参
比如我们每次想累计的数字 是动态传入的。
|add 10| 0 |dec 10|
这个时候,点击add 或者 dec 增、减时需要传递对应的数字作为目标,那么这个目标值在组件中需要提交action时传递过去。
主要修改两处:
- 在reducers的同步修改方法中,添加2号参数action,它是一个对象。下边的payload属性会挂载上传来的实参。
store/modules/counterStore.js
//...
reducer:{
addToNum(state,action){
state.count = action.payload
}
dec(state,action){
state.count = action.payload
}
}
//...
- 组件调用点处,传入实参。
app.jsx
//...
function App(){
//...
return(
<div className="App">
<button onClick={()=> dispatch(dec(10))}>- 10</button>
{count}
<button onClick={()=> dispatch(add(10))}>+ 10</button>
</div>
)
}
异步状态操作store
从后台获取数据存入到store,异步获取
数据,然后再异步修改
数据。
接下来,我们做一个案例:动态获取列表数据,然后渲染到页面。
创建对应的子模块store文件:
│├── store 状态机配置
││├── index.js 状态机入口
││└── modules 状态机模块
│ │ └── ListDataStore.js
步骤:
-
创建store的写法保持不变,配置好同步修改状态的方法
-
单独封装一个函数,在函数内部return一个新函数,在新函数中
- 2.1 封装异步请求获取数据
- 2.2 调用同步方法action传入接口数据,并使用dispatch提交
-
组件中dispatch的写法保持不变
ListDataStore.js
import { createSlice } form '@reduxjs/toolkit'
const counterStore = createSlice({
name:'listData',
//初始化state
initialState:{
listData:[]
},
//修改状态的方法, 同步方法, 支持直接修改
reducer:{
setListData(state,action){
state.listData = action.payload
},
}
})
// 解构出来actionCreater 函数
const {setListData} = counterStore.actions
// 异步请求,固定的写法,return 一个异步函数,函数内做ajax请求。
const fetchListData = ()=>{
return async (dispatch)=>{
const res = await axios.get('url')
dispatch(setListData(res.data || []))//提交action时传参
}
}
// 获取reducer
const reducer = counterStore.reducer
// 以按需导出的方式导出actionCreater
export {fetchListData}
// 以默认的方式导出reducer
export default reducer
核心代码:
// 解构出来actionCreater 函数
const {setListData} = counterStore.actions
// 异步请求,固定的写法,return 一个异步函数,函数内做ajax请求。
const fetchListData = ()=>{
return async (dispatch)=>{
const res = await axios.get('url')
dispatch(setListData(res.data || []))//提交action时传参
}
}
然后在index.js 里注册引入子store.
页面里如何调用渲染呢?
App.js
import { useDispatch,useSelector } from 'react-redux'
import { add,dec} from './store/modules/counterStore'
import {fetchListData} from './store/modules/listDataStore'//1.
import {useEffect} from 'react'//3.
function App(){
const { count } = useSelector(state => state.counter)
const { listData } = useSelector(state => state.listData)//2.
const dispatch = useDispatch()
useEffect(()=>{//4.
dispatch(fetchListData())
},[dispatch])
return(
<div className="App">
<button onClick={()=> dispatch(dec())}>-</button>
{count}
<button onClick={()=> dispatch(add())}>+</button>
{/*5.*/}
<ol>
{listData.map(item => <li key={item.id}>{item.name}</li>)}
</ol>
</div>
)
}
export default App
Redux-DevTools 的安装
谷歌设置,扩展程序。
记录每次action的提交,以及时间。
数据以 tree chart raw(json) 形式的展示。