redux 传统状态管理 vs redux tookit

2021-08-10  本文已影响0人  Riya

Redux传统流程:

export default createStore(
  combineReducers({
    test,
    counter,
    cnode
  }),
  compose(applyMiddleWare(thunk), applyMiddleWare(logger))
)
reducers -> counter.js
import produce from 'immer'
const initState = {
  num: 0
}
// reducer是纯函数, 不能修改入参
// 本质是由switch语句构成的函数 -> 深拷贝state,修改拷贝后的值,抛出(使用immer中间件做深复制)
export default function(state=initState, {type,payload}) {
//state -> 要深复制的值
// newState -> 深复制后的结果
  return produce(state, newState=>{
    switch (type) {
      case COUNTER_NUM_ADD:
// 修改深复制之后的结果
        newState.num += payload
        break
      case COUNTER_NUM_SUB:
        newState.num -= payload
        break;
      default:
    }
// 将修改后的结果抛出
    return newState
  })
}
reducers -> counter.js
import produce from 'immer'
import {
  GET_CNODE_LIST
} from '../types'

const initState = {
  list: []
}
export default function(state=initState, {type,payload}){
  return produce(state, newState=>{
    switch (type) {
      case GET_CNODE_LIST:
// 调接口后获得的数据
        console.log('cnode list', payload);
        newState.list = payload
        break
      default:
    }
    return newState
  })
}
//类组件写法,使用修饰符@
import React from "react";

import { connect } from ".react-redux";
import { updateMsg } from '@/store/actions'

function mapStateToProps({test}) {
  return {
    msg: test.msg
  }
}
function mapDispatchToProps(dispatch) {
  return {
    updateMsg: payload => dispatch(updateMsg(payload))
  }
}
// mapStateToProps 映射state放到props上
// mapDispatchToProps 映射dispath放到props上
@connect(mapStateToProps, mapDispatchToProps)
class PanelA extends React.Component {
  updateMsg() {
    this.props.updateMsg(Math.random())
  }
  render() {
    console.log('PanelA 类组件的props', this.props);
    return (
      <div>
        <h1>在类组件中使用Redux数据</h1>
        <h1>{this.props.msg}</h1>
        <button onClick={()=>this.updateMsg()}>change MSG</button>
      </div>
    )
  }
}
export default PanelA
//函数式组件写法
import React from 'react'
import { connect } from 'react-redux'
import { updateMsg } from '@/store/actions'

export default connect(
  ({test})=>({
    msg: test.msg
  }),
  dispatch=>({
    // updateMsg(payload)返回一个action
    updateMsg: payload=>dispatch(updateMsg(payload))
  })
)(props => {
    return (
    <div>
      <h1>在函数式组件中使用redux数据</h1>
      <h1>{props.msg}</h1>
      <button onClick={()=>props.updateMsg('hello 朱晓玥')}>修改msg</button>
    </div>
  )
  }
)
// 标准做法 hooks写法
import React from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { updateMsg } from '@/store/actions'
export default () => {
  const msg = useSelector(({test})=>test.msg)
  const dispatch = useDispatch()
  return (
    <div>
      <h1>在函数式组件中使用redux数据</h1>
      <h1>{msg}</h1>
      <button onClick={()=>dispatch(updateMsg(Math.random()))}>修改msg</button>
    </div>
  )
}
import {
  COUNTER_NUM_ADD,
  COUNTER_NUM_SUB,
} from '../types'
import { fetchCnode } from '@/api'
// 返回整体action type+payload
// action生成器(creator)
export function addNum(payload) {
  return {
    type: COUNTER_NUM_ADD,
    payload
  } 
}
export function subNum(payload) {
  return {
    type: COUNTER_NUM_SUB,
    payload
  } 
}
// 使用thunk插件
// 调接口结束后再触发dispatch方法,将调接口获取到的值,传到reducer中,更新状态管理中的数据
export function getList(payload) {
  return dispatch => {
    // 调接口
问题:
在这里有一个坑,这里只是获得了调接口后的数据,但是还没有改变状态管理中的数据
初始化时,在视图中dispatch(fetchCnode(values)),接着在后面使用状态管理中调接口获取到的数据
会是undefined,这时候很可能状态管理还没有运行到改变数据时,视图就已经更新
解决方法:
在状态管理中设置一个标记success,初始值为false,状态管理中数据更新后,同时将success改为true
在视图中判断数据是否在状态管理中更新,使用success判断即可

    fetchCnode(payload).then(list=>{
      console.log('actions list data', list);
      dispatch({
        type: GET_CNODE_LIST,
        payload: list
      })
    })
  }
}
// actions type 的字典,视图中和reducer中使用
export  const COUNTER_NUM_ADD = 'COUNTER_NUM_ADD'
export  const COUNTER_NUM_SUB = 'COUNTER_NUM_SUB'
export  const GET_CNODE_LIST = 'GET_CNODE_LIST  '

redux tookit

import thunk from 'redux-thunk'
import login from './reducer/login'
import { configureStore } from '@reduxjs/toolkit'
import logger from 'redux-logger'
export const store = configureStore({  
  reducer: {
    login
  },
// 不要直接写数组,否则会把toolkit中封装的默认middleware覆盖
// 可以不加thunk,redux-toolkit默认安装了thunk
  middleware: getDefaultMiddleware => [...getDefaultMiddleware(), thunk, logger ]
})

export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch

export default store
export const loginSlice = createSlice({
  name: 'login',
  initialState: {
    token : localStorage.getItem('token'),
    user: { },
    success: false,
    color: ''
  },
// 同步reducer(不需要调接口的reducer)
  reducers: {
    addColor(state, action) {
      console.log(action.payload,'addcolor-----')
      state.cardColor = action.payload
    }
  },
// toolkit封装,将同步reducer变为异步reducer
  extraReducers: (builder) => {
    builder.addCase (login.fulfilled, (state, action)=>{
      // 直接修改state(tookit中封装了immer,在内部已经做过深复制了)
      console.log('----login', action);
      state.token = action.payload
    })
// fulfilled 表示调接口成功的状态,总共有三种状态
    .addCase(getUserInfo.fulfilled, (state, action)=>{
      state.user = action.payload
    })
    // .addCase(...)
  }
})
// 在子reducer中,一定要抛出一个reducer(实际上是一个由switch构成的函数)
export default loginSlice.reducer
// 抛出异步reducer
export { getUserInfo }
// 抛出同步reducer
export const { addColor } = cardSlice.actions
// 在视图中通过dispatch(login(params))
// 成功时,执行对应的case,并修改state,更新视图
const login = createAsyncThunk(
  // 信号
  'login/tologin', //type
  // 函数(action)
  async (data) => {
    const res = await loginApi(data)
    const token = res.token
    localStorage.setItem('token', token)
    return token  //payload
  }
)
const getUserInfo = createAsyncThunk(
  'login/getUserInfo', //type
  async (params) => {
    const res = await getUserInfoApi(params)
    return res  //payload
  }
)
import { Provider } from 'react-redux'
function App() {
  return (
    <Provider store={store}>
      <DashBoard />
    </Provider>
  )
}
export default App;
import { useAppSelector, useAppDispatch } from '@/hooks'
export default () => {
  const dispatch = useAppDispatch()
  const token = useAppSelector(({login})=>login.token)
  useEffect(()=>{
    if(token) {
      dispatch(getUserInfo(token))
    }
  },[token])
}

ps:需要自己封装hooks

import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'
import type { RootState, AppDispatch } from '@/store'

// Use throughout your app instead of plain `useDispatch` and `useSelector`
export const useAppDispatch = () => useDispatch<AppDispatch>()
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector
上一篇 下一篇

猜你喜欢

热点阅读