Redux
-
react:全局状态管理 -
安装
npm install redux
核心
state:存放数据reducer:修改仓库数据actions:派发行为触发reducer
初体验
-
1、安装
npm install redux -
2、在
src>store>index.js中创建仓库// 导入redux import { createStore } from "redux"; // 定义reducer function reducer(state = { age: 12 }, actions) { switch (actions.type) { case 'add': console.log('add:', state); return { age: state.age + 1 }; default: return state } } // 创建仓库 let stort = createStore(reducer) export default stort -
组件中使用仓库
import stort from "./store/index"; // 引入仓库 import { useState } from "react"; // 引入react中的hooks function App() { // 1、获取仓库中的数据 console.log('获取整个仓库的数据:', stort.getState()); let [age, setAge] = useState(stort.getState().age) // 2、修改数据 const btn = () => { // 2.1、触发dispatch stort.dispatch({ type: 'add' }) // 2.2、仓库里的数据已经更新=>动态数据(和仓库关联) setAge(stort.getState().age) } return ( <div> <h1>仓库数据:{age}</h1> <button onClick={() => btn()}>修改仓库数据</button> </div> ); } export default App;
创建仓库
-
使用
redux中的createStore来创建仓库,引入:import {createStore} from 'redux' -
语法:
let stort = createStore(reducer,中间件) -
什么是reducer?
1.reducer是一个函数 2.reducer函数有2个参数(形参) 参数1:默认数据 参数2:触发reducer方法传入的实参(触发reducer方法`dispatch`传递的参数,一个对象) 3.reducer返回值为最新的数据 4.只能处理同步问题(相当于vuex中的mutations) -
stort
@@observable: ƒ observable() // 观察仓库中的数据 dispatch: ƒ dispatch(action) // 触发reducer getState: ƒ getState() // 获取到仓库中的数据 replaceReducer: ƒ replaceReducer(nextReducer) // 替换reducer subscribe: ƒ subscribe(listener) // 订阅
获取仓库数据
-
语法
stort.getState() stort.getState().age
模块化(combineReducers)
-
作用:合并reducer,使用仓库数据模块化
-
引入
react中提供的combineReducersimport { combineReducers } from 'redux' -
使用:
let reducers = combineReducers({属性:各自的reducer,属性:各自的reducer...}) // 使用 -
模块化目录结构
|src | |store | | |-index.js(创建仓库) | | |reducers | | | |-index.js(合并reducer) | | | |-CartReducer.js(购物车模块reducer) | | | |-MeReducer.js(我的模块reducer) | | | |-PrdReducer.js(产品模块reducer)
// 合并reducer
import { combineReducers } from 'redux' // 合并reducer
// 引入reducers
import CartReducer from "./CartReducer";
import PrdReducers from "./PrdReducers";
import MeReducer from "./MeReducer";
// 合并reducer
let reducers = combineReducers({
CartReducer,
PrdReducers,
MeReducer
})
export default reducers
// 创建仓库
import { createStore } from "redux"; // 导入redux
import reducers from './reducers' // 导入合并的reducers
let store = createStore(reducers) // 创建仓库
export default store // 暴露
//组件中使用
// 引入仓库
import stort from "./store/index";
// 引入react中的hooks
import { useState } from "react";
function App() {
#// 获取仓库中的数据
console.log('获取所有仓库的数据:', stort.getState());
let [meAge, setAge] = useState(stort.getState().MeReducer.age) #// 获取数据
let [cartList, setList] = useState(stort.getState().CartReducer.cartList) #//获取数据
// 修改MeReducer数据
const btn1 = () => {
// 触发dispatch
stort.dispatch({ type: 'meAdd' })
// 仓库里的数据已经更新=>动态数据(和仓库关联)
setAge(stort.getState().MeReducer.age)
}
// 修改CartReducer数据
const btn2 = () => {
// 触发dispatch
stort.dispatch({ type: 'cartAdd' })
// 2.2、仓库里的数据已经更新=>动态数据(和仓库关联)
setList(stort.getState().CartReducer.cartList)
}
return (
<div>
<h1>Me数据:{meAge}</h1>
<h3>Cart数据:{cartList}</h3>
<button onClick={() => btn1()}>修改Me数据</button>
<button onClick={() => btn2()}>修改Cart数据</button>
</div>
);
}
工程化处理reducer合并(模块化)
-
作用:自动引入reducer,自动合并
-
使用
webpack提供的require.context来自动获取文件中暴露的内容-
语法:
let webf = require.context('文件路径',布尔值,正则) 参数: 参数1:要导入模块的文件夹目录 参数2:是(true)否(false)搜索当前目录的子目录 参数3:要匹配的文件 提供2个方法: 获取到文件路径:webf.keys() => ['文件路径','文件路径',...] 获取到文件的内容:webpack(路径).default
-
//工程化处理,自动合并reducer
import { combineReducers } from 'redux' // 合并reducer
// 使用webpack提供的require.context自动获取文件中暴露的内容
let webf = require.context('./', true, /\.js/)
let list = webf.keys() // 获取到文件路径数组
// 删除自己(index.js)
let index = list.findIndex((item, index) => {
return item == './index.js'
})
list.splice(index, 1)
// 组合成对象
let objs = {}
list.forEach((item, index) => {
// 处理item >> ./MeReducer.js => MeReducer
let items = item.replace('./', '').replace('.js', '')
objs[items] = webf(item).default //添加键值对
})
// 合并rducer
let reducers = combineReducers(objs)
export default reducers
创建仓库和组件中使用 与 原生的模块化一致
自动派发reducer行为(bindActionCreators)
-
使用
react提供的bindActionCreators来实现自动派发行为import { bindActionCreators } from "redux"; -
使用语法:
let meActions = bindActionCreators({属性:reducer方法,...}, dispatch) let meActions = bindActionCreators({ meAdd, reduce }, store.dispatch) -
自动派发行为目录结构
|src | |store | | |-index.js(创建仓库) | | |reducers | | | |-index.js(合并reducer) | | | |-CartReducer.js(购物车模块reducer) | | | |-MeReducer.js(我的模块reducer) | | | |-PrdReducer.js(产品模块reducer) | | |actions | | | |-cartActions(购物车行为) | | | |-meActions(我的行为) | | | |-prdActions(产品行为) -
在
store>actions中实现reducer和行为一一对应// Me模块行为 import { bindActionCreators } from "redux"; // 引入自动触发 import store from "../index"; // 引入store // 创建派发方法 let meAdd = () => { return { //行为actions type: 'meAdd' } } let reduce = () => { return { //行为actions type: 'reduce' } } // 使用bindActionCreators let meActions = bindActionCreators({ meAdd, reduce }, store.dispatch) export default meActions // 暴露// 组件中使用 import stort from "./store/index"; // 引入仓库 import { useState } from "react"; // 引入react中的hooks // 使用自动派发 import meActions from "./store/actions/meActions"; import cartActions from "./store/actions/cartActions"; import prdActions from "./store/actions/prdActions"; // 组件 function App() { // 获取仓库中的数据 console.log('获取所有仓库的数据:', stort.getState()); let [meAge, setAge] = useState(stort.getState().MeReducer.age) let [cartList, setList] = useState(stort.getState().CartReducer.cartList) // 修改MeReducer数据 const btn1 = () => { #// 触发dispatch meActions.meAdd() // 仓库里的数据已经更新=>动态数据(和仓库关联) setAge(stort.getState().MeReducer.age) } // 修改CartReducer数据 const btn2 = () => { #// 触发dispatch cartActions.cartAdd() // 2.2、仓库里的数据已经更新=>动态数据(和仓库关联) setList(stort.getState().CartReducer.cartList) } return ( <div> <h1>Me数据:{meAge}</h1> <h3>Cart数据:{cartList}</h3> <button onClick={() => btn1()}>修改Me数据</button> <button onClick={() => btn2()}>修改Cart数据</button> </div> ); } export default App;
项目中使用(react-redux)
-
react-redux提供了1个组件和2个hooks- 组件:
<Provider />
- 组件:
-
作用:在父级组件中提供数据
-
hooks:
useSelector:获取仓库数据useDispatch:触发reducer行为
-
安装
npm install react-redux
react-redux提供的组件和hooks
-
引入:
// 引入组件 import { Provider } from "react-redux"; // 引入hooks import { useSelector, useDispatch } from "react-redux"; -
组件:
<Provider />-
作用:在父级组件中提供数据
-
语法:
<Provider store={store}> <!-- 子级组件 --> </Provider>
-
-
hooks:
-
useSelector:获取仓库数据-
语法:
let stateData = useSelector(state=>{return 返回值}) state:所有仓库数据 返回值:仓库中的数据
-
-
useDispatch:触发reducer行为-
使用:
let dispatch = useDispatch() 底层: 1.触发reducer 2.获取到仓库的最新数据 3.通过useState更新 -
语法:
dispatch(行为) dispatch({ type: 'meAdd' })
-
-
🌰案例:
// 父级组件提供数据(index.js)
...
import { Provider } from "react-redux"; // 引入react-redux中的Provider
import store from "./store"; // 引入store
...
root.render(
<Provider store={store}>
<App />
</Provider>
);
// 自己组件中获取仓库中的数据(App.js)
import { useSelector, useDispatch } from "react-redux"; // 引入react-redux提供的2个hooks
function App() {
// 获取仓库中的数据
let meAge = useSelector(state => {
return state.MeReducer.age
})
let cartList = useSelector(state => {
return state.CartReducer.cartList
})
console.log('获取数据:', meAge, cartList);
// 使用useDispatch
let dispatch = useDispatch() // 1.触发reducer 2.获取到仓库的最新数据 3.通过useState更新
console.log('组件重新创建');
return (
<div>
<h1>Me数据:{meAge}</h1>
<h3>Cart数据:{cartList}</h3>
<!-- 触发reducer行为 -->
<button onClick={() => dispatch({ type: 'meAdd' })}>修改Me数据</button>
<button onClick={() => dispatch({ type: 'cartAdd' })}>修改Cart数据</button>
</div>
);
}
export default App;
异步actions
-
本质:写一个函数劫持
-
为何需要处理异步?
因为reducer只能出合理同步问题 在项目中可能需要在仓库中发送请求,获取到异步数据 -
异步actions的作用?
1.获取到异步数据 2.触发reducer修改长裤中的数据 -
实现异步actions
1.在store文件夹下创建actions文件夹 2.实现actions和各自的reducer一一对应// 我的模块中的异步处理 import axios from 'axios' import store from '../index' // 定义异步处理方法 export function getAdd(){ // 1.获取异步数据方法 function getData(){ return axios.get('').then(res=>{ //后端给的数据 let a = 100 // 假设这是后端数据 return a }) } // 2.触发reducer return async()=>{ let data = await getData() // 获取异步数据 store.dispatch({type:'add',data }) // 触发reducer } }// 组件中 <button onClick={()=>dispatch( getAdd())}>触发异步actions</button> -
在组件中使用dispatch触发actions
报错。 需要使用 中间件 和 redux-thunk
redux-thunk
安装:npm i redux-thunk
作用:处理异步actions => 异步actions触发方法的第一个参数就是dispatch
-
配置中间件=>redux-thunk
// 在store/index.js中进行配置 // 1、引入redux中的applyMiddleware方法 import {createStore,applyMiddleware} from 'redux' import reducers from './reducers' let store = createStore(reducers,applyMiddleware(ReduxThunk)) // 2、引入redux-thunk import ReduxThunk from 'redux-thunk'-
applyMiddleware(ReduxThunk)的作用-
让组件中的
dispatch()中可以写方法 -
return返回函数的第一个参数就是dispatch// 我的模块中的异步处理 import axios from 'axios' import store from '../index' // 定义异步处理方法 export function getAdd(){ // 1.获取异步数据方法 function getData(){ return axios.get('').then(res=>{ //后端给的数据 let a = 100 // 假设这是后端数据 return a }) } // 2.触发reducer return async(dispatch)=>{ // 第一个参数就是dispatch let data = await getData() // 获取异步数据 dispatch({type:'add',data }) // 触发reducer } }<button onClick={()=>dispatch( getAdd())}>触发异步actions</button>
-
-
-
总结redux-thunk
- 组件中的
dispatch()中可以写方法 - 异步actions返回(return)的方法的第一个形参就是
dispatch
- 组件中的
Toolkit
-
Redux-Toolkit:- 全局状态管理
- 本质:
redux+redux-thunk进行封装
-
安装
# npm i @reduxjs/toolkit -
创建仓库
// store/index.js // 1、下包 npm install @reduxjs/toolkit react-redux // 2、创建仓库 import { configureStore,createSlice } from '@reduxjs/toolkit' const store = configureStore({ reducer: { // 模块化对象 me:meReducer, cart:cartReducer, limit:LimitReducer } }) export default store -
各自的reducer:存放数据和定义仓库行为
// store/modules/MeState.js import {createSlice} from '@reduxjs/toolkit' let MeState = createSlice({ name:'me', // 作用域,唯一标识 initialState:{ // 仓库存放的数据 name:'乐乐', age:18 }, // 定义修改仓库数据的行为 reducers:{ // 只能处理同步问题 changeName(state,val){ // 参数1:该仓库存放的(模块)数据 state.name = val.payload }, addAge(state){ state.age +=1 } } }) //暴露行为,本质就是将方法暴露,"行为在actions中" export let { changeName,addAge} = MeState.actions export default MeState.reducer-
configureStore语法作用:创建仓库 configureStore的返回值:就是store仓库 合并reducer:configureStore({各自的reducer}) -
createSlice语法作用:创建各自的reducer 返回值:一个对象 createSlice({实例属性})
-
-
仓库数据和react项目关联
-
安装react-redux
# npm i react-redux -
在入口文件中引入
import store from './store/index' import {Provider} from 'react-redux' const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <Provider store={store}> <App /> </Provider> );
-
-
组件中使用仓库中的数据
// 引入react-redux提供的两个方法 import {useSelector,useDispatch} from 'react-redux' // 引入reducer行为 // 注意:行为需要暴露处理,这些行为就是reducers中定义的方法 import {changeName} from './store/modules/MeState' function App(){ let {name} = useSelector(store=>store.meReducer) let dispatch = useDispatch() return( <div> <h2>获取到仓库中的数据{name}</h2> <button onClick={()=>dispatch(changeName('小乐'))}>修改数据</button> </div> ) } export default App
异步actions
1.获取到异步数据
2.触发reducer修改仓库中的数据
- 使用第三方插件
redux-thunk
import {createSlice} from '@reduxjs/toolkit'
import axios from 'axios'
// 全局数据和行为
let LimitState = createSlice({
name:'cart',
initialState:{
router:[]
},
reducers:{ //只能处理同步问题的问题
addRouter(state,data){
state.router= data.payload
}
}
})
// 暴露两个:行为=>组件使用直接引入;reducer=>模块划分
export let {addRouter} = LimitState.actions
export default LimitState.reducer
// 自己定义异步actions
export function getRouterData(){ //异步actions
// 获取异步数据的方法
function getData(){
return axios.get('').then(res=>{
let routerList = [1,2,3,4,5] // 假设后端数据
return routerList
})
}
// 触发reducer行为
return async (dispatch)=>{ // return第一个行参就是dispatch
let data = await getData() // 获取后端数据
dispatch(addRouter(data)) // 触发行为
}
}