美文网首页
Redux - 给vuex开发者

Redux - 给vuex开发者

作者: 我叫Aliya但是被占用了 | 来源:发表于2021-03-17 14:42 被阅读0次

    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]);
    
    • reducer(state: State, action: Action): State 作用相当于 mutation,入口处只支持一个

      • Action: { type: any, payload: any }
    • preloadedState: 初始的 state

    • getState: 获取 state,只能获取整个 state

    • dispatch(action: Action): void : 触发 reducer

      • Action: { type: any, payload: any }
    • subscribe(() => void): 监听到 dispatch 调用(reducer 执行)后回调,比如用来更新视图。

    例子:

    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);
    
    • mapStateToProps(state, ownProps) => props:每次存储状态更改时调用(会触发 rerender)。它接收整个存储状态,并应返回此组件需要的数据对象。

    • mapDispatchToProps :此参数可以是函数或对象。

      • 如果是函数,则在创建组件时将调用一次。它将 dispatch 作为参数接收,并应返回一个对象,该对象具有 dispatch 用于分派动作的完整功能。

      • 如果它是一个由动作创建者组成的对象,则每个动作创建者都将变成 prop 函数,该函数在被调用时会自动调度其动作。注意:我们建议使用此“对象简写”形式。

    与 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
    }
    
    • takeEvery, takeLatest区别是后者仅执行最后一个异步方法,即不支持并发。

    • call 很像 js 原生 call(它会自己 next?)

    • put 触发 dispatch

    • 多个 sagaFn 可以

      function* allSageFn() {
        yield all([sagaFn1(), sagaFn2()]);
      }
      sagaMiddleware.run(allSageFn);
      

    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 状态,但它应该能够监听到任何动作类型。例如,“用户注销”时清除数据。???

    异步操作:

    • redux-thunk:复杂同步逻辑 + 简单异步(toolkit推荐)

    • redux-saga:复杂异步逻辑,基于 ES6 generator 控制粒度更细

    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 格式化返回值

    相关文章

      网友评论

          本文标题:Redux - 给vuex开发者

          本文链接:https://www.haomeiwen.com/subject/wwqecltx.html