Redux
大约 5 分钟
redux
安装
yarn add react-redux
基本使用
借助react-redux提供的组件Provider,全局注入store
import React from 'react'
import ReactDOM from 'react-dom/client'
// import App from './App'
import Vote from './Vote'
// 导入redux仓库
import store from './store'
import { Provider } from 'react-redux'
ReactDOM.createRoot(document.getElementById('root')).render(
<Provider store={store}>
<Vote />
</Provider>
)
为组件注入store
import React, {useEffect, useState} from 'react'
import store from './store'
import { connect } from 'react-redux'
function VoitMain(props) {
const { sup, dis } = props
return (
<div>
<h3 style={{ 'display': 'flex', 'justifyContent': 'space-between' }}>
<span>赞成: {sup}人</span>
<span>反对: {dis}人</span>
</h3>
</div>
)
}
export default connect(
store => store.vote
)(VoitMain)
为组件注入dispatch
import React from 'react'
import action from './store/action'
import { connect } from 'react-redux'
function VoteFoot(props) {
const { voteSup, voteDis } = props
return (
<div style={{ 'display': 'flex', 'justifyContent': 'space-between' }}>
<button onClick={voteSup}>赞成</button>
<button onClick={voteDis}>反对</button>
</div>
)
}
export default connect(
null,
action.vote
)(VoteFoot)
在页面使用react-redux重构
商品页
import React, { useEffect } from "react";
import axios from "axios";
import Seller from "./components/Seller";
import { connect } from "react-redux";
import action from "./store/action";
function Goods(props) {
useEffect(() => {
props.sellerInit()
}, [])
return (
<div className="goods-wrapper">
<div className="seller-info">
<Seller />
</div>
<div>商品页</div>
<div>购物车</div>
</div>
);
}
export default connect(null, action.seller)(Goods);
goods.jsx
import cloneDeep from "lodash/cloneDeep";
import * as Types from "../action.type";
const initState = {};
const sellerReducer = (state = initState, action) => {
// 克隆一次
state = cloneDeep(state);
switch (action.type) {
case Types.SELLER_INIT:
state = action.value
break;
}
return state;
};
export default sellerReducer;
sellerReducer
import axios from 'axios'
import * as Types from '../action.type'
export default {
async sellerInit(data) {
const res = await axios.get('http://129.211.169.131:5000/shop/seller')
return {
type: Types.SELLER_INIT,
value: res.data.data
}
}
}
sellerAction, 配合redux-promise使用
创建仓库,将项目的状态保存在仓库里面
import { createStore } from 'redux'
function reducer(state={}, action) {
// 拷贝一次state
state = cloneDeep(state)
// 根据这个action的type,修改state数据 ---> vuex mutation
// action.type 你就类比成vuex的mutation的名字
switch(action.type){
case action的type:
state.xx = action.value
}
return state
}
// 创建一个仓库,接收一个函数(reducer)
const store = createStore()
组件拿到仓库数据
- 拿到仓库对象
- 调用仓库对象的某一个方法拿到数据: getState, 拿到的就是reducer的第一个参数
// 通过导入的方式拿到仓库
import store from './store'
function Vote() {
const 数据 = store.getState()
// 拿到数据,去渲染页面
}
组件中修改数据
拿到仓库对象
调用仓库提供的一个方法来改数据:dispatch(行为对象)
// 通过导入的方式拿到仓库 import store from './store' function Vote() { return ( <button onClick={ () => store.dispatch({type: '行为类型', value:参数}) }>该数据</button> ) }
数据变化了,组件更新
数据变化了,redux就会依次执行事件池里面的函数
基于上诉的机制,我们需要把要更新组件的更新函数,放入redux的事件池
// 通过导入的方式拿到仓库 import store from './store' function Vote() { const 数据 = store.getState() // 让redux定于该组件的更新方法 const [obj, setObj] = useState({}) useEffect(() => { store.subscribe(() =>setObj({})) }, []) // 拿到数据,去渲染页面 }
redux工程化
reducer的拆分与合并 ---》 类比成 vuex中的module
- 在store下创建一个文件夹:reducer
import cloneDeep from 'lodash/cloneDeep'
import * as Types from '../action.type'
const initState = {
sup: 1,
opp: 1
}
// 接收state,返回一个新的state(新的地址)
function voteReducer(state = initState, action) {
// 深拷贝一下state
state = cloneDeep(state)
// 根据action行为对象中的type,然后改变state
switch (action.type) {
case Types.VOTE_SUP:
state.sup += action.value
break
case Types.VOTE_OPP:
state.opp += action.value
break
}
return state
}
export default voteReducer
在reducer文件夹下面写一个一个的reducer, votereducer
统一管理action ---> 类比成vuex的action
- 在store下创建一个文件夹: action,里面放一个一个的action
import * as Types from '../action.type'
export default {
voteSup(num) {
// 发起ajax
// 类比成 vuex的action提交mutation
return {type: Types.VOTE_SUP, value: num}
},
voteOpp(num) {
return {type: Types.VOTE_OPP, value: num}
}
}
投票的action
实现一个商品首页
商家信息交给redux管理。
创建一个商家信息的reducer
import cloneDeep from 'lodash/cloneDeep'
import * as Types from '../action.type'
// 商家信息初始化
const initState = {}
function sellerReducer(state = initState, action) {
state = cloneDeep(state)
// 商家的mutation
switch(action.type) {
// 初始化商家信息
case Types.SELLER_INIT:
state = action.value
break
}
return state
}
export default sellerReducer
注册商家信息的reducer(vuex里面注册模块!): store/reducer/index.js
// 将项目中的所有reducer合并成一个,并导出
import { combineReducers } from 'redux'
// 导入
import sellerReducer from './sellerReducer'
// 合并reducer并导出
export default combineReducers({
// 类比成vuex注册模块: seller就是模块名, sellerReducer模块的值
seller: sellerReducer
})
编写seller的action
import * as Types from '../action.type'
export default {
sellerInit(seller) {
// seller就是在组件里面请求好数据,发过来
// 类比成vuex里面:action提交mutation
return {
type: Types.SELLER_INIT,
value: seller
}
}
}
配置redux中间件,让action支持返回一个promise
安装一个redux的中间件: redux-promise
- yarn add redux-promise
注册redux-promise, store/index.js
// 创建一个仓库,用于保存投票组件的状态( {sup: 1, opp: 1} ) import { createStore, applyMiddleware} from 'redux' import reducer from './reducer' // 让action支持返回proimise import reduxPromise from 'redux-promise' // 创建仓库, 接收一个参数: reducer是一个函数 const store = createStore(reducer, applyMiddleware(reduxPromise)) export default store
上诉配置好之后,action就支持返回一个promise, 改写sellerAction
import * as Types from '../action.type' import axios from 'axios' export default { // seller 数据 async sellerInit(seller) { const res = await axios.get('http://1.15.179.44:3001/shop/seller') // 提交了一个sellerReucer下的mutation, return { type: Types.SELLER_INIT, value: res.data.data } } }
商品列表页实现
在store/action.type.js定义类型
// 商品列表初始化
export const GOODS_INIT = 'GOODS_INIT'
定义goodsReducer
import cloneDeep from 'lodash/cloneDeep'
import * as Types from '../action.type'
const initState = []
function goodsReducer(state = initState, action) {
// 拷贝一下state
state = cloneDeep(state)
// 定义改变数据
switch(action.type) {
// 初始化商品列表
case Types.GOODS_INIT:
state = action.value
break
}
return state
}
export default goodsReducer
一定要在store/reducer/index 里面进行注册
定义goodsAction
// 导出一个一个的action方法
import * as Types from '../action.type'
export default {
initGoods() {
// 请求完后台数据
return { type: Types.GOODS_INIT, value: }
}
}