跳至主要內容

Redux

xiaoye大约 5 分钟ReactReactRedux

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:  }
    }
}