【问题标题】:A pattern to manage intial async actions with redux sagas for fetching initial data?使用 redux sagas 管理初始异步操作以获取初始数据的模式?
【发布时间】:2019-10-29 14:26:20
【问题描述】:

到目前为止,我一直在使用 redux-thunk 进行异步操作。在应用程序启动时,我曾经不得不从某个服务器加载一些数据。所以我要做的是创建异步操作,然后使用 async/await 来了解它们何时完成。在获取异步操作时,我会渲染一个启动画面。当他们完成后,我开始申请。

现在我正在切换到 redux sagas,但我不知道如何使用它们。我不能使用异步/等待。我的想法是在需要获取数据的商店的每个对象中都有一个布尔变量。但是我想知道是否有任何模式可以以干净的方式管理它。有人知道为此目的的任何模式吗?

// example with thunks

import { someAsyncAction, someAsyncAction2 } from './actions';

const initialDispatches = async (store) => {
  await store.dispatch(someAsyncAction());
  await store.dispatch(someAsyncAction2());
};

export default initialDispatches;

【问题讨论】:

    标签: reactjs redux fetch redux-thunk redux-saga


    【解决方案1】:

    在我看来,在这种情况下没有正确/错误的模式。

    我为你举了一个例子,说明如何使用 saga 实现你的目标。

    基本思想:每个资源都有一个单独的 saga(例如,我曾经拆分为特征 saga),以及一个用于初始化的 saga。 然后主根 saga 将并行运行它们,您将能够在应用程序的某处触发初始化 saga 并让这一切发生:

    注意:这个例子非常天真和简单,你应该找到一个更好的方法来组织所有东西,我只是尽量保持简单。

    const {Provider, connect} = ReactRedux;
    const {createStore, applyMiddleware} = Redux;
    const createSagaMiddleware = ReduxSaga.default;
    const {takeEvery, takeLatest} = ReduxSaga;
    const {put, call, all, fork} = ReduxSaga.effects;
    
    const initialState = {
        fruits: [],
      vegtables: []
    };
    
    const reducer = (state = initialState, action) => {
        switch (action.type) {
        case 'SET_FRUITS':
            return {
            ...state,
            fruits: [
                ...action.payload.fruits
            ]
          }
        case 'SET_VEGTABLES':
            return {
            ...state,
            vegtables: [
                ...action.payload.vegtables
            ]
          }
      }
        return state;
    };
    
    //====== VEGTABLES ====== //
    async function fetchVegtables() {
        return await new Promise((res) => {
        setTimeout(() => res([
            'Cuecumber',
          'Carrot',
          'LEttuce'
        ]), 3000)
      });
    }
    
    function* getVegtables() {
        const vegtables = yield call(fetchVegtables);
      yield put({ type: 'SET_VEGTABLES', payload: { vegtables } })
    } 
    
    function* vegtablesSaga() {
        yield takeEvery('GET_VEGTABLES', getVegtables);
    }
    //====== VEGTABLES ====== //
    
    //====== FRUITS ====== //
    async function fetchFruits() {
        return await new Promise((res) => {
        setTimeout(() => res([
            'Banana',
          'Apple',
          'Peach'
        ]), 2000)
      });
    }
    
    function* getFruits() {
        const fruits = yield call(fetchFruits);
      console.log(fruits)
      yield put({ type: 'SET_FRUITS', payload: { fruits } })
    } 
    
    function* fruitsSaga() {
        yield takeEvery('GET_FRUITS', getFruits);
    }
    //====== FRUITS ====== //
    
    //====== INIT ====== //
    function* initData() {
        yield all([
        put({ type: 'GET_FRUITS' }),
        put({ type: 'GET_VEGTABLES' })
      ]);
    }
    
    function* initSaga() {
        yield takeLatest('INIT', initData);
    }
    //====== INIT ====== //
    
    // Sagas
    function* rootSaga() {
      yield all([
        yield fork(initSaga),
        yield fork(fruitsSaga),
        yield fork(vegtablesSaga),
      ]);
    }
    
    // Component
    class App extends React.Component {
        componentDidMount() {
        this.props.dispatch({ type: 'INIT' });
      }
      render () {
        return (
                <div>
                  <div>fruits: {this.props.fruits.join()}</div>
            <div>vegtables: {this.props.vegtables.join()}</div>
                </div>
        );
      }
    }
    
    // Store
    const sagaMiddleware = createSagaMiddleware();
    const store = createStore(
      reducer,
      applyMiddleware(sagaMiddleware)
    );
    
    sagaMiddleware.run(rootSaga);
    
    const ConnectedApp = connect((state) => ({
        fruits: state.fruits,
      vegtables: state.vegtables
    }))(App);
    
    // Container component
    ReactDOM.render(
      <Provider store={store}>
        <ConnectedApp />
      </Provider>,
      document.getElementById('root')
    );
    

    如您所见,我有两种资源:水果和蔬菜。 每个资源都有自己的 saga,负责监视在某处调度的 GET 动作。 他们每个人都使用基本的 saga 效果(例如 call、put 等)来异步获取资源,然后将其分派到 store(然后 reducer 处理它们)。

    此外,我设置了一个 initSaga,它使用 all 效果以并行方式触发所有资源获取 saga。

    您可以在此处查看运行的整个示例:

    https://jsfiddle.net/kadoshms/xwepoh5u/17/

    【讨论】:

    • 非常感谢您的回答!但是我找不到知道传奇何时结束的方法。无论如何都是有用的,因为您确认这种情况没有模式。我会按照我的想法使用布尔变量。再次感谢
    【解决方案2】:

    我写过关于在 redux-saga 之上创建一个结构以通过提供初始操作然后根据操作结果加载/成功/错误状态来促进异步操作的文章。它分为两部分,首先是同步,然后是异步。

    它基本上可以让你以声明的方式编写你的 reducer,就像一个对象一样。您只需调用初始操作,其余的由 saga 负责,您的 UI 可以在触发加载/成功/错误操作时响应结果。下面是减速器的样子。

    const counterAsync = {
      initialState: {
        incrementAsync_result: null,
        incrementAsync_loading: false,
        incrementAsync_success: false,
        incrementAsync_error: false,
      },
    
      incrementAsync: {
        asyncOperation: incrementAPI,
        action: ({number}) => {
          type: ACTION_INCREMENT_ASYNC,
          payload: {
            number: number
          }
        }
        loading: {
          action: (payload) => {
            return {
              type: ACTION_INCREMENT_ASYNC,
              payload: { ...payload }
            }
          },
          reducer: (state, action) => {
            state.incrementAsync_loading = true
            state.incrementAsync_success = false
            state.incrementAsync_error = false
          }
        },
        success: {
          action: (payload) => {
            return {
              type: ACTION_INCREMENT_ASYNC,
              payload: { ...payload }
            }
          },
          reducer: (state, action) => {
            state.incrementAsync_result = action.payload
            state.incrementAsync_loading = false
            state.incrementAsync_success = true
            state.incrementAsync_error = false
          }
        },
        fail: {
          action: (payload) => {
            return {
              type: ACTION_INCREMENT_ASYNC,
              payload: { ...payload }
            }
          },
          reducer: (state, action) => {
            state.incrementAsync_result = action.payload
            state.incrementAsync_loading = false
            state.incrementAsync_success = false
            state.incrementAsync_error = true
          }
        }
      },
    }
    

    我们在工作中使用了这种模式的重量级稍重的版本,它比 vanilla redux/saga 好得多。

    如果您有任何问题,请告诉我!

    https://medium.com/@m.razajamil/declarative-redux-part-1-49a9c1b43805 https://medium.com/@m.razajamil/declarative-redux-part-2-a0ed084e4e31

    【讨论】:

    • 谢谢!你的方法和我想的很接近。我会做类似的事情:)
    猜你喜欢
    • 2018-06-03
    • 2016-09-04
    • 1970-01-01
    • 1970-01-01
    • 2016-09-22
    • 1970-01-01
    • 2016-09-20
    • 2019-01-29
    • 2018-01-10
    相关资源
    最近更新 更多