使用Redux Sagas管理初始异步操作以获取初始数据的模式吗?

时间:2019-06-15 02:07:29

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

直到现在,我一直在使用redux-thunk进行异步操作。在应用程序启动时,我不得不从某些服务器加载一些数据。因此,我要做的是创建异步操作,然后使用异步/等待以了解它们何时完成。在获取异步操作时,我呈现了一个启动画面。他们完成后,我将启动应用程序。

现在,我正在切换到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;

2 个答案:

答案 0 :(得分:0)

我认为在这种情况下没有正确/错误的模式。

我为您提供了一个使用传奇如何实现目标的示例。

基本思想:每个资源都有一个单独的传奇(例如,我以前将其分解为特征sagas),而初始化则有一个传奇。 然后,主根传奇将并行运行它们,您将能够在应用程序中的某个地方触发初始化传奇,并使其全部发生:

注意:该示例非常幼稚且简单,您应该找到一种更好的方式来组织所有内容,我只是想使其保持简单。

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')
);

如您所见,我有两种资源:水果和蔬菜。 每个资源都有其自己的传奇,负责监视在某处分发的 GET 操作。 它们中的每个都使用诸如调用,放置等基本的传奇效果来异步获取资源,然后将其分派到商店(然后由reducer处理)。

此外,我还设置了 initSaga ,该 initSaga 使用 all 效果来触发所有资源以并行方式获取Sagas。

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

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

答案 1 :(得分:0)

我写过关于在redux-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
      }
    }
  },
}

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

让我知道您是否有任何疑问!

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