目录
4、redux + react-redux + 分模块+ 异步操作
使用状态管理器的前提:多个视图依赖同一状态,来自不同的视图行为需要变更同一状态
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;