React状态管理器

目录

1、基础redux

2、redux结合react-redux

3、redux结合react-redux分模块使用

4、redux + react-redux + 分模块+ 异步操作

5、redux toolkit

       使用状态管理器的前提:多个视图依赖同一状态,来自不同的视图行为需要变更同一状态

1、基础redux

redux属于js的状态管理模式,不独属于react

npm i redux -S

(1)创建store文件夹下index.js 创建状态管理器

import { createStore } from 'redux'
//创建reducer以及初始化状态
const reducer = (state={
  msg:'hello redux',
  count:10
},action)=>{
  // action中type代表动作的标识,用于触发行为,payload代表传递的参数
  switch(action.type){
    case 'CHANGE_MSG': //type名字随意
      return { ...state,msg:action.payload}
    case 'INCRERMENT_COUNT': 
      return { ...state,count:state.count + action.payload}
    case 'REDUCE_COUNT': 
      return  Object.assign({}, state, { count: state.count - action.payload })
      default:
      return state
  }
}
//创建状态管理器
const store = createStore(reducer)
export default store

(2)修改index.js文件 订阅数据的变化,重新渲染视图

// 订阅数据的变化,重新渲染视图
 store.subscribe(()=>{
  root.render(<App />)
 })

(3)编写App.tsx

import React from 'react';
import Child1 from './Child1';
import Child2 from './Child2';
const App = () => {
  return (
    <div>
      <Child1></Child1>
      <hr />
      <Child2></Child2>
    </div>
  );
};

export default App;

(4)子组件Child1和Child2使用状态管理器

import React from 'react';
//引入状态管理器
import store from './store';

const Child1 = () => {
//获取状态管理器中的状态
  const state = store.getState()
  return (
    <div>
      <h3>Child1</h3>
      <div>
        { state.msg } - { state.count }
        //修改状态
        <button onClick={ ()=>{
          store.dispatch({
            type:'CHANGE_MSG',
            payload:'hello stay calm'
          })
        }}>修改msg</button>
        <button onClick={ ()=>{
          store.dispatch({
            type:'INCRERMENT_COUNT',
            payload:10
          })
        }}>修改count增加10</button>
        <button onClick={ ()=>{
          store.dispatch({
            type:'REDUCE_COUNT',
            payload:10
          })
        }}>修改count减少10</button>
      </div>
    </div>
  );
};

export default Child1;
import React from 'react';
import store from './store';

const Child2 = () => {
  const state = store.getState()
  return (
    <div>
      <h3>Child2</h3>
      <div>
        { state.msg } - { state.count }
        <button onClick={ ()=>{
          store.dispatch({
            type:'CHANGE_MSG',
            payload:'hello stay calm'
          })
        }}>修改msg</button>
        <button onClick={ ()=>{
          store.dispatch({
            type:'INCRERMENT_COUNT',
            payload:10
          })
        }}>修改count增加10</button>
      </div>
    </div>
  );
};

export default Child2;

2、redux结合react-redux

npm i redux react-redux -S

(1)创建store文件夹index.js 

import { createStore } from 'redux'

const reducer = (state = {
  proList: [],
  kindList: []
}, { type, payload }) => { // action  解构了 { type, payload }
  switch (type) {
    case 'CHANGE_PRO_LIST':
      return { ...state, proList: payload }
    case 'CHANGE_KIND_LIST':
      return { ...state, kindList: payload }
    default:
      return state
  }
}

const store = createStore(reducer)

export default store

(2)修改index.js文件 订阅数据的变化,重新渲染视图 导入Provider组件

root.render(
  <Provider store = { store }>
    <App/>
  </Provider>
)

(3)App.jsx

import React from 'react';
import Home from './Home'
import Kind from './Kind'
const App = () => {
  return (
    <div>
      <h1>redux+react-redux</h1>
      <Home></Home>
      <Kind/>
    </div>
  );
};

export default App;

(4)子组件引入connect

//Home.jsx
/* 业务组件写到组件中 */
import React, { useEffect } from 'react';
/* Connect可以理解为高阶组件 实际上connect的返回值为高阶组件*/
import { connect } from 'react-redux';
/* 展示组件负责数据的展示发起指令 */
const Home = (props) => {
  console.log(props);//dispatch  proList
  const { proList,dispatch } =props
  useEffect(()=>{
    fetch('http://xxxxxxxx:3001/api/pro/list').then(res => res.json()).then(res => {
      console.log(res.data);
      dispatch({
        type:'CHANGE_PRO_LIST',
        payload:res.data
      })
    })
  },[dispatch])//不要直接使用props  先将props解构 添加依赖项
  return (
    <div>
      <h1>home</h1>
      <ul>
        {
          proList && proList.map(item=>(
            <li key={ item.proid }>{ item.proname}</li>
          ))
        }
      </ul>
    </div>
  );
};
/* mapStateToProps负责给展示组件提供数据 提供给展示组件的props属性 */
const mapStateToProps = (state)=>{
  return {  //必须含有返回值
    proList:state.proList
  }
}
/* 返回一个容器组件 负责给展示组件提供数据 以及执行业务逻辑*/
export default connect(mapStateToProps)(Home);
//Kind.jsx
import React, { useEffect } from 'react';
import { connect } from 'react-redux';
const Kind = (props) => {
  console.log(props);
  const { kindList,getKindListData} = props
  useEffect(() => {
    // 展示组件发起指令,后续交给容器组件处理
    getKindListData()
  }, [getKindListData])
  return (
    <div>
      <h1>kind</h1>
      {
        kindList && kindList.map(item => {
          return (
            <p key = { item }> { item } </p>
          )
        })
      }
    </div>
  );
};
const mapStateToProps=(state)=>{
  return{
    kindList:state.kindList
  }
}
// 将业务逻辑交给容器组件组件处理
const mapDispatchToProps = (dispatch) => { // dispatch 为默认参数,可以通过此 触发 reducer 中状态的更新
  return {
    getKindListData () { // 返回自定义函数,供 展示组件调用,展示组件通过 props 访问即可
      fetch('http://xxxxxxxx/api/pro/categorylist').then(res => res.json()).then(res => {
        console.log(res.data) 
        // 修改状态
        dispatch({
          type: 'CHANGE_KIND_LIST',
          payload: res.data
        })
      })
    }
  }
}
export default connect(mapStateToProps,mapDispatchToProps)(Kind) ;

3、redux结合react-redux分模块使用

(1)创建store文件夹,内置modules文件夹,创建home.js和kind.js

//home.js
/* 单独管理home模块 */
const reducer = (state={
  bannerList:[],
  proList:[]
},{type,payload})=>{
  switch (type) {
    case 'CHANGE_BANNER_LIST':
      return {...state,bannerList:payload}
    case 'CHANGE_PRO_LIST':
      return {...state,proList:payload}
    default:
      return state
  }
}

export default reducer
//kind.js
/* 单独管理kind模块的状态 */
const reducer = (state={
  kindList:[],
},{type,payload})=>{
  switch (type) {
    case 'CHANGE_KIND_LIST':
      return {...state,kindList:payload} 
    default:
      return state
  }
}

export default reducer

(2)stroe/index.js,合并子文件

import {
  combineReducers,
  createStore
} from "redux";
import home from "./modules/home";
import kind from "./modules/kind";

const reducer = combineReducers({
  home,
  kind
})
const store = createStore(reducer)

export default store

(3)修改入口文件

root.render(
  <Provider store = { store }>
    <App/>
  </Provider>
)

(4)创建子组件

//Kind.jsx
import {React,useEffect} from 'react';
import { connect } from 'react-redux';
  /* 业务逻辑在容器组件中 */
const Kind = connect(
  ({ kind: { kindList }}) => ({ kindList }), // state => { return { kindList: state.kind.kindList }}
  (dispatch) => ({
    getKindListData () {
      fetch('http://××××××××××:3001/api/pro/categorylist').then(res => res.json()).then(res => {
        dispatch({
          type: 'CHANGE_KIND_LIST',
          payload: res.data
        })
      })
    }
  })
)(({ kindList, getKindListData }) => {
  useEffect(() => {
    getKindListData()
  }, [getKindListData])
  return (
    <div>
      <h1>Kind</h1>
      {
        kindList && kindList.map(item => {
          return (
            <p key = { item }> { item } </p>
          )
        })
      }
    </div>
  );
});
export default Kind;
import React, { useEffect } from 'react';
import { connect } from 'react-redux'
const Home = ({ bannerList, proList, dispatch }) => {
  /* 业务逻辑在展示型组件中 */
  useEffect(() => {
    fetch('http://××××××××××:3001/api/banner/list').then(res => res.json()).then(res => {
      dispatch({
        type: 'CHANGE_BANNER_LIST',
        payload: res.data
      })
    })
    fetch('http://××××××××××:3001/api/pro/list').then(res => res.json()).then(res => {
      dispatch({
        type: 'CHANGE_PRO_LIST',
        payload: res.data
      })
    })
  }, [dispatch])
  return (
    <div>
      <h1>Home</h1>
      <div>
        { 
          bannerList && bannerList.map(item => (
            <img key = { item.bannerid } src={ item.img } alt={item.alt} style={{ height: 100 }} />
          ))
        }
      </div>
      <ul>
        {
          proList && proList.map(item => {
            return (<li key = { item.proid }>{ item.proname }</li>)
          })
        }
      </ul>
    </div>
  );
};
export default connect(
  /* 结构赋值解构 */
  // (state)=>({bannerList:state.home.bannerList,proList:state.home.proList})
  // (home)=>({bannerList:home.bannerList,proList:home.proList})
  // ({home:{bannerList,proList}})=>({bannerList:bannerList,proList:proList})
  ({home:{bannerList,proList}})=>({bannerList,proList})
  )(Home);

(4)渲染

import React from 'react';
import Home from './views/Home'
import Kind from './views/Kind'
const App = () => {
  return (
    <div>
      <Home></Home>
      <hr />
      <Kind></Kind>
    </div>
  );
};

export default App;

4、redux + react-redux + 分模块+ 异步操作

4.1 redux-thunk

npm i redux react-redux redux-thunk -S

(1)创建store文件夹,modules文件夹 

//modules/home.js
const reducer = (
  state = {
    bannerList: [],
    proList: []
  },
  { type, payload }
) => {
  switch (type) {
    case 'CHANGE_BANNER_LIST':
      return { ...state, bannerList: payload }
    case 'CHANGE_PRO_LIST':
      return { ...state, proList: payload }
    default:
      return state
  }
}

export default reducer
//modules/kind.js
const reducer = (
  state = {
    kindList: []
  },
  { type, payload }
) => {
  switch (type) {
    case 'CHANGE_KIND_LIST':
      return { ...state, kindList: payload }
    default:
      return state
  }
}

export default reducer

(2)整合reducer 

//store/index.js
import { createStore, combineReducers, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'

import home from './modules/home'
import kind from './modules/kind'

const reducer = combineReducers({
  home,
  kind
})

// 第二个参数表示代码中含有异步操作,且异步操作需要提取出 容器组件
const store = createStore(reducer, applyMiddleware(thunk))

export default store

(3)修改入口文件

root.render(
  <Provider store = { store }>
    <App/>
  </Provider>
)

(4) 封装api

//api/home.js
export function getBannerList () {
  return fetch('http://######:3001/api/banner/list').then(res => res.json())
}

export function getProList (params) {
  return fetch('http://######:3001/api/pro/list?limitNum=' + params.limitNum).then(res => res.json())
}
//api/kind.js
export function getKindList () {
  return fetch('http://######:3001/api/pro/categorylist').then(res => res.json())
}

(5)创建store/actions文件夹,容器组件抽离异步操作

//home.js
import {getBannerList,getProList } from '../../api/home'
const action = {
  /* 接口不需要参数 dispatch为默认参数 */
  getBannerListActon(dispatch) {
    getBannerList().then(res => {
      dispatch({
        type: 'CHANGE_BANNER_LIST',
        payload: res.data
      })
    })
  },
  /* 接口需要参数 返回一个函数 函数默认参数为dispatch  */
  getProListActon(params) {
    return (dispatch) => {
      getProList(params).then(res => {
        dispatch({
          type: 'CHANGE_PRO_LIST',
          payload: res.data
        })
      })
    }
  }

}

export default action
//kind.js
import { getKindList } from '../../api/kind'
const action = {
  getKindListAction(dispatch){
    getKindList().then(res=>{
      dispatch({
        type:'CHANGE_KIND_LIST',
        payload:res.data
      })
    })
  }
}
export default action

(6)创建页面组件views文件夹

//Home.jsx
import { connect } from 'react-redux';
import React, { useEffect } from 'react';
// import {getBannerList,getProList } from '../api/home'
/* 异步操作 */
import action from '../store/actions/home'
const Home = ({bannerList,proList,getBannerListData,getProListData}) => {
  console.log('1111',bannerList);
  console.log('1111',proList);
  useEffect(() => {
    getBannerListData()
    getProListData(5)
  }, [getBannerListData, getProListData])
  return (
    <div>
      <h1>home</h1>
      <div>
        { 
          bannerList && bannerList.map(item => (
            <img key = { item.bannerid } src={ item.img } alt={item.alt} style={{ height: 100 }} />
          ))
        }
      </div>
      <ul>
        {
          proList && proList.map(item => {
            return (<li key = { item.proid }>{ item.proname }</li>)
          })
        }
      </ul>
    </div>
  );
};

export default connect(
  ({home:{bannerList,proList}})=>({bannerList,proList}),
  (dispatch)=>({
    getBannerListData(){
      // getBannerList().then(res=>{
      //   dispatch({
      //     type:'CHANGE_BANNER_LIST',
      //     payload:res.data
      //   })
      // })
      /* 按照actionCreate规则写 无参不加() */
      dispatch(action.getBannerListActon)
    },
    getProListData(val){
      // getProList(val).then(res=>{
      //   dispatch({
      //     type:'CHANGE_PRO_LIST',
      //     payload:res.data
      //   })
      // })
      dispatch(action.getProListActon({limitNum:8}))
    }
  })
)(Home);
//Kind.jsx
import { connect } from 'react-redux';
import React, { useEffect } from 'react';
import {getKindList } from '../api/kind'
const Kind = ({kindList,getKindListData}) => {
  console.log('1111',kindList);
  useEffect(()=>{
    getKindListData()
  },[getKindListData])
  return (
    <div>
      <h1>kind</h1>
      {
        kindList && kindList.map(
          item=>{
            return (
              <p key={ item.proid }>{ item }</p>
            )
          }
        )
      }
    </div>
  );
};

export default connect(
  ({kind:{kindList}})=>({kindList}),
  (dispatch)=>({
    getKindListData(){
      getKindList().then(res=>{
        dispatch({
          type:'CHANGE_KIND_LIST',
          payload:res.data
        })
      })
    }
  })
)(Kind);
//APP.jsx
import React from 'react';
import Home from './views/Home'
import Kind from './views/Kind'
const App = () => {
  return (
    <div>
      <Home></Home>
      <hr />
      <Kind></Kind>
    </div>
  );
};

export default App;

4.2 redux-saga

npm i redux-saga -S

(1)store/modules文件夹子目录如上api文件夹如上,修改入口文件如上 APP.jsx如上

(2) store/mySaga,js store/index.js

// put 类似 dispatch put({ type: '', payload: ''}) 触发状态的修改
// call 用来调用数据请求   call(getBannerList)  ===>    getBannerList()
// takeLatest 用来响应触发哪一个异步操作
import { put, call, takeLatest } from 'redux-saga/effects'

import { getBannerList, getProList } from '../api/home'
import { getKindList } from '../api/kind'

// 异步操作
function * getBannerListAction () {
  const res = yield call(getBannerList)
  console.log('banner', res.data)
  // 修改状态
  yield put({
    type: 'CHANGE_BANNER_LIST',
    payload: res.data
  })
}

function * getProListAction (action) {
  console.log(111, action)
  const res = yield call(getProList, action.payload)
  console.log('pro', res.data)
  yield put({
    type: 'CHANGE_PRO_LIST',
    payload: res.data
  })
}

function * getKindListAction () {
  const res = yield call(getKindList)
  console.log('kind', res.data)
  yield put({
    type: 'CHANGE_KIND_LIST',
    payload: res.data
  })
}

// 定义异步触发的条件
function * mySaga () {
  // 组件触发 REQUEST_BANNER 即可执行 getBannerListAction 异步行为
  yield takeLatest('REQUEST_BANNER', getBannerListAction)
  // 组件触发 REQUEST_PRO 即可执行 getProListAction 异步行为
  yield takeLatest('REQUEST_PRO', getProListAction)
   // 组件触发 REQUEST_KIND 即可执行 getKindListAction 异步行为
  yield takeLatest('REQUEST_KIND', getKindListAction)
}

export default mySaga
import { createStore, combineReducers, applyMiddleware } from 'redux'
import createSagaMiddleWare from 'redux-saga' // 导入生成中间件的函数

import mySaga from './mySaga' // 异步行为

// 分模块
import home from './modules/home'
import kind from './modules/kind'

const reducer = combineReducers({ home, kind }) // 整合reducer

const middleware = createSagaMiddleWare() // 生成中间件

const store = createStore(reducer, applyMiddleware(middleware)) // 创建状态管理器

middleware.run(mySaga) // 中间件使用异步,放到store创建之后

export default store

(3)组件页面调用

//Home.jsx
import React from 'react';
import { useEffect } from 'react';
import { connect } from 'react-redux'
const Home = ({ bannerList, proList, dispatch }) => {
  useEffect(() => {
    // 触发某个行为,从而触发 异步操作
    dispatch({ type: 'REQUEST_BANNER' })
    dispatch({ type: 'REQUEST_PRO', payload: { limitNum: 3 } })
  }, [dispatch])
  return (
    <div>
      <h1>Home</h1>
      <div>
        { 
          bannerList && bannerList.map(item => (
            <img key = { item.bannerid } src={ item.img } alt={item.alt} style={{ height: 100 }} />
          ))
        }
      </div>
      <ul>
        {
          proList && proList.map(item => {
            return (<li key = { item.proid }>{ item.proname }</li>)
          })
        }
      </ul>
    </div>
  );
};

export default connect(
  ({ home: { bannerList, proList }}) => ({ bannerList, proList })
)(Home);
//Kind.jsx
import React, { useEffect } from 'react';
import { connect } from 'react-redux'
const Kind = ({ kindList, getKindListData }) => {
  useEffect(() => {
    getKindListData()
  }, [getKindListData])
  return (
    <div>
      <h1>Kind</h1>
      {
        kindList && kindList.map(item => {
          return (
            <p key = { item }> { item } </p>
          )
        })
      }
    </div>
  );
};

export default connect(
  ({ kind: { kindList }}) => ({kindList}),
  (dispatch) => ({
    getKindListData () {
      dispatch({ type: 'REQUEST_KIND' })
    }
  })
)(Kind);

5、redux toolkit

npm i @reduxjs/toolkit redux react-redux redux-devtools -S

(1)创建状态管理器,分模块切片

//store/modules/home.js
import { createSlice  } from "@reduxjs/toolkit";
// 创建切片
export const homeSlice = createSlice({
  name:'home',
  initialState:{
    bannerList:[],
    proList:[]
  },
  reducers:{
    changeBannerList(state,action){
      state.bannerList = action.payload
    },
    changeProList(state,action){
      state.proList = action.payload
    }
  }
})

export const {changeBannerList,changeProList } = homeSlice.actions

export default homeSlice.reducer
//store/modules/kind.js
import { createSlice } from "@reduxjs/toolkit";

export const kindSlice = createSlice({
  name:'kind',
  initialState:{
    kindList:[]
  },
  reducers:{
    changeKindList(state,action){
      state.kindList = action.payload
    }
  }
})

export const { changeKindList } = kindSlice.actions
export default kindSlice.reducer
//store/index.js
import { configureStore } from '@reduxjs/toolkit'
import home from './modules/home'
import kind from './modules/kind'
const store = configureStore({
  reducer:{
    home,
    kind
  }
})

export default store

(2)入口文件配置如上 ,api文件如上,App.jsX如上

  (3) 组件中使用状态管理器

//views/Home.jsx
import React, { useEffect } from 'react';
/* useSelector 获取状态管理器的状态 */
/* useDispatch 用来修改状态 */
import { useSelector,useDispatch } from 'react-redux';
import { getBannerList,getProList } from '../api/home'
import { changeBannerList, changeProList } from '../store/modules/home';
const Home = () => {
  const bannerList = useSelector(state=>state.home.bannerList)
  const proList = useSelector(state=>state.home.proList)
  const dispatch = useDispatch()
  useEffect(()=>{
    getBannerList().then(res => { 
      dispatch(changeBannerList(res.data))
    })
    getProList({limitNum:5}).then(res=>{
      dispatch(changeProList(res.data))
    })
  },[dispatch])
  return (
    <div>
      <h1>home</h1>
      <div>
        { 
          bannerList && bannerList.map(item => (
            <img key = { item.bannerid } src={ item.img } alt={item.alt} style={{ height: 100 }} />
          ))
        }
      </div>
      <ul>
        {
          proList && proList.map(item => {
            return (<li key = { item.proid }>{ item.proname }</li>)
          })
        }
      </ul>
    </div>
  );
};

export default Home;
//views/Kind.jsx
import React from 'react';
import { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux'
import { getKindList } from '../api/kind'
import { changeKindList } from '../store/modules/kind'
const Kind = () => {
  const kindList = useSelector(state => state.kind.kindList)
  const dispatch = useDispatch() 
  useEffect(() => {
    getKindList().then(res => {
      dispatch(changeKindList(res.data))
    })
  }, [dispatch])
  return (
    <div>
      <h1>Kind</h1>
      {
        kindList && kindList.map(item => {
          return (
            <p key = { item }> { item } </p>
          )
        })
      }
    </div>
  );
};

export default Kind;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

stay calm~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值