Redux - 给vuex开发者

2021-03-17  本文已影响0人  我叫Aliya但是被占用了

react语法 - 给vue开发者
Dva - react状态管理 - 给vuex开发者
React Router 5.x - 给vuex开发者

redux 官方 API阮一峰的网络日志

非响应式,不支持异步。

有 state、reducer(mutation)概念

创建 store

import { createStore } from "redux";
let { subscribe, dispatch, getState } = createStore(reducer, [preloadedState]);

例子:

const reducer = (state = 0, action) => {
  switch (action.type) {
    case "INCREMENT":
      return state + 1;
    case "DECREMENT":
      return state - 1;
    default:
      return state;
  }
};
let { subscribe, dispatch, getState } = createStore(reducer);

const Counter = ({ value, onIncrement, onDecrement }) => (
  <div>
    <h1>{value}</h1>
    <button onClick={onIncrement}>+</button>
    <button onClick={onDecrement}>-</button>
  </div>
);

const render = () => {
  ReactDOM.render(
    <Counter
      value={getState()}
      onIncrement={() => dispatch({ type: "INCREMENT" })}
      onDecrement={() => dispatch({ type: "DECREMENT" })}
    />,
    document.getElementById("root")
  );
};

render();
subscribe(render);

处理复杂 reducer: combineReducers

import { combineReducers, createStore } from "redux";

const todos = (state: string[] = [], action: actionType) => {
  if (action.type === "add") return [...state, action.payload];
  else return state;
};
const filterKeyword = (state: string = "", action: actionType) => {
  if (action.type === "filter") return action.payload || "";
  else return state;
};
const reducer = combineReducers({ todos, filterKeyword });

export const store = createStore(reducer); // { todos: [], filterKeyword: '' }

npm install react-redux

react-redux 官方 API

import { Provider } from "react-redux";
import { createStore } from "redux";
let store = createStore(reducer, [preloadedState]); // 参数如上

ReactDOM.render(
  <Provider store={store}>
    {" "}
    // 根部注入
    <App />
  </Provider>,
  document.getElementById("root")
);

与 react 组件关联:使用 connect(mapStateToProps, mapDispatchToProps)(Component)

import { connect } from "react-redux";

type propsType = {
  num: number,
  dispatch: Dispatch,
};

const App: React.FC<propsType> = (props: propsType) => {
  const { dispatch, num } = props;

  return <div onClick={dispatch({ type: "add" })}>{num}</div>;
};

connect((state) => ({ num: state.a || 1 }))(comp1);

与 react 组件关联:使用 useSelector 和 useDispatch

// import { connect } from "react-redux";
import { useSelector, useDispatch } from "react-redux";

type propsType = {
  num: number,
  dispatch: Dispatch,
};

const App: React.FC<propsType> = (props: propsType) => {
  // const { dispatch, num } = props;
  const dispatch = useDispatch();
  const num = useSelector((state) => state.a || 1); // 返回不是对象

  return <div onClick={dispatch({ type: "add" })}>{num}</div>;
};

vuex 与 react-redux 对照表

VUEX dva
根部 new Vue({store }) 注入 <Provider store={store}><app/></Provider>
namespace 利用 combineReducers 分模块
state reducer 上的参数 state 默认值
getters 利用 connect、useSelector 整理数据
mutations reducers (只有一个入口;return 完整的 state 以更新)
actions 需要第三方 redux-saga 支持,本质是对指定reducer劫持
commit dispatch
subscribe(监听 mutation 调用) --

处理异步 redux-saga

基于 Generator 和 redux 的中间件机制

import { applyMiddleware, createStore } from 'redux';

const store = createStore(
  reducer,
  defualtVal,
  applyMiddleware(中间件1, 中间件2 ...)
);

applyMiddleware 会把中间件们依序洋葱式包裹 dispatch。redux-thunkredux-sage都是一个用来处理异步的中间件。

// 简单例子
import { createStore, applyMiddleware } from "redux";
import createSagaMiddleware from "redux-saga";

// 创建sage中间件
const sagaMiddleware = createSagaMiddleware();
const store = createStore(reducer, applyMiddleware(sagaMiddleware));
// 注册异步事件们
sagaMiddleware.run(sagaFn); // * sagaFn
// sagaFn为一个方法,run时执行,一般用来(使用takeEvery)劫持dispatch
import { put, call, takeEvery, takeLatest, all } from "redux-saga/effects";
function* sagaFn() {
  // 使用 takeEvery 劫持 async_add action
  yield takeEvery("async_add", asyncAdd);
}

function* asyncAdd() {
  const res = yield call(axios.post, "/api/test", { uid: 123 });
  yield put({ type: "add", payload: res }); // 触发正常的 add dispatch
}

Redux Toolkit @reduxjs/toolkit

另一种对 Redux 的封装,此工具包中还包含一个强大的数据获取和缓存功能(RTK Query

create-react-app 默认集成了它

import { configureStore } from '@reduxjs/toolkit'

export const store = configureStore({
  reducer: { counter: counterReducer, …… }, // 切片们
})

import { Provider } from 'react-redux'

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
)

创建 counterReducer 切片

import { createSlice } from '@reduxjs/toolkit'

export const counterSlice = createSlice({
  name: 'counter',
  initialState: {
    value: 0,
  },
  reducers: {
    add: (state) => {
      // 实际上不会改变状态,因为它使用Immer库,
      // 它检测对“草稿状态”的更改,并根据这些更改生成一个全新的不可变状态
      state.value += 1
    },
  },
})

export const { add } = counterSlice.actions // 导出actions

export default counterSlice.reducer // 导出切片

调用方法:

import { useSelector, useDispatch } from 'react-redux'
import { add } from './counterSlice'

export function Counter() {
    const count = useSelector((state) => state.counter.value)
    const dispatch = useDispatch()
  
  return (
    <button onClick={() => dispatch(add())}>
        点击+1,现在为:{count}
    </button>
  )
}

[TS写法在这里](https://redux-toolkit.js.org/tutorials/typescript)和这里

Redux 操作类型并不意味着对单个 slice 来说是专有的。从概念上讲,每个 slice reducer “拥有”自己的 Redux 状态,但它应该能够监听到任何动作类型。例如,“用户注销”时清除数据。???

异步操作:

import { configureStore } from '@reduxjs/toolkit'

export const store = configureStore({
  reducer: { counter: counterReducer, …… }, // 切片们
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware({
      serializableCheck: false, // 关闭可序列化检查
    }),
  devTools: process.env.NODE_ENV !== "production",
})
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'

// First, create the thunk
const thunkUserList = createAsyncThunk('users/query', async (pageIndex) => API.getUserList(pageIndex))

// Then, handle actions in your reducers:
const usersSlice = createSlice({
  name: 'users',
  initialState: { entities: [], loading: 'idle' },
  reducers: {
    // standard reducer logic, with auto-generated action types per reducer
  },
  extraReducers: (builder) => {
    // Add reducers for additional action types here, and handle loading state as needed
    builder
      .addCase(thunkUserList.pending, state => state.loading = true)
      .addCase(thunkUserList.fulfilled, (state, action) => { 
        state.loading = false
        state.entities.push(action.payload);
      })
  },
})

// Later, dispatch the thunk as needed in the app
dispatch(thunkUserList(123))

根据 createEntityAdapter 格式化返回值

上一篇 下一篇

猜你喜欢

热点阅读