Использование redux-saga и повторного набора на стороне сервера

Прямо сейчас я пытаюсь получить начальное состояние моей стороны сервера приложений с помощью повторного набора.

Повторный набор запускает чистое действие объекта, а redux-saga слушает/ожидает этого действия, а затем запускает асинхронный запрос.

Но проблема в том, что у Redial нет обещаний для разрешения, когда сага о прибавлении завершена, потому что он отправляет чистый объект.

Компонент

const redial = {
   fetch: ({ dispatch }) => dispatch({ type: actionTypes.FETCH_START }),
};

export default class PostList extends Component {
    render() {
        const { posts } = this.props;
        return (
            <div>
                {posts.map(post => <ListItem key={post.id} post={post} />)}
            </div>
        );
    }
}

PostList.propTypes = {
    posts: PropTypes.array.isRequired,
};

export default provideHooks(redial)(connect(mapStateToProps)(PostList));

Сага

export function *fetch() {
    try {
        yield put({ type: actionTypes.FETCH_START });
        const response = yield call(fakeData);
        yield put({ type: actionTypes.FETCH_SUCCESS, data: response.data });
        yield put({ type: actionTypes.FETCH_PENDING });
    } catch (e) {
        yield put({ type: actionTypes.FETCH_FAIL });
    }
}

export default function *loadPost() {
    yield * takeLatest(actionTypes.FETCH_START, fetch);
}

export default function *rootSaga() {
    yield [
        fork(loadPost),
    ];
}

Есть ли способ подключить redial к redux-saga?


person cusX    schedule 01.05.2016    source источник


Ответы (2)


Я думаю, что это можно сделать следующим образом:

во-первых, вам нужно добавить магазин в locals. (коды взяты из README повторного набора)

const locals = {
  path: renderProps.location.pathname,
  query: renderProps.location.query,
  params: renderProps.params,

  // Allow lifecycle hooks to dispatch Redux actions:
  dispatch,
  store
};

Затем вы можете создать обещание вручную следующим образом:

const redial = {
   fetch: ({ store, dispatch }) => {
       return new Promise((resolve, reject) => {
           const unsubscribe = store.subscribe(()=>{
               if (store.getState()...) { // monitor store state changing by your saga
                   resolve(...) //you probably dont need any result since your container can read them from store directly
                   unsubscribe();
               }
               if (store.getState()....error) {
                   reject(...)
                   unsubscribe();
               }
           });
           dispatch({ type: actionTypes.FETCH_START }),
       }
   }
};

Эти коды предназначены только для демонстрации, не используйте их в производстве без надлежащего тестирования.

Я думаю, что может быть более элегантный способ отслеживать результаты выполнения саги, чем проверять состояние хранилища избыточности снова и снова, пока состояние не будет соответствовать операторам if(...), возможно, вы можете запустить сагу с хранилищем избыточности и внешними прослушивателями, а затем эти повторные наборы крючкам не нужно знать о структуре вашего магазина.

person Shawn Hu    schedule 14.12.2016

Есть довольно элегантный способ сделать это. Прежде всего вам нужно создать реестр для ваших задач саги (помните, что запуск метода .run промежуточного программного обеспечения возвращает дескриптор задачи):

export default class SagaTaskRegistry {
  constructor() {
    this._taskPromises = [];
  }

  addTask(task) {
    if (!this._taskPromises) {
      this._taskPromises = [];
    }
    this._taskPromises.push(task.done);
  }

  getPromise() {
    return new Promise((resolve) => {
      const promises = this._taskPromises;
      if (!promises) {
        resolve();
        return;
      }
      this._taskPromises = undefined;
      Promise.all(promises).then(resolve).catch(resolve);
    }).then(() => {
      const promises = this._taskPromises;
      if (promises) {
        return this.getPromise();
      }
      return undefined;
    });
  }
}

Когда вы добавляете новые задачи в промежуточное ПО saga с помощью .run, вы затем вызываете registryInstance.add(taskDescriptor). SagaTaskRegistry возьмет обещание для этой задачи и добавит его в массив.

Вызвав getPromise, вы получите обещание, которое будет разрешено, когда все добавленные задачи будут завершены. Оно никогда не будет отклонено, так как вы, скорее всего, не хотите, чтобы неудачная выборка приводила к отклонению — вы все равно хотите отображать свое приложение с состоянием ошибки.

И вот как вы можете комбинировать его с redial:

import createSagaMiddleware from 'redux-saga';
import { applyMiddleware, createStore } from 'redux';
import rootReducer from 'your/root/reducer';
import yourSaga from 'your/saga';

const sagaMiddleware = createSagaMiddleware();
const middleWare = [sagaMiddleware];
const createStoreWithMiddleware = applyMiddleware(...middleWare)(createStore);
const store = createStoreWithMiddleware(rootReducer);
const sagaTaskRegistry = new SagaTaskRegistry();
const sagaTask = sagaMiddleware.run(yourSaga);
sagaTaskRegistry.addTask(sagaTask);

match({ routes, history }, (error, redirectLocation, renderProps) => {
  const locals = {
    path: renderProps.location.pathname,
    query: renderProps.location.query,
    params: renderProps.params,
    dispatch: store.dispatch,
  };

  trigger('fetch', components, locals);

  // Dispatching `END` will force watcher-sagas to terminate,
  // which is required for the task promises to resolve.
  // Without this the server would never render anything.
  // import this from the `redux-saga` package
  store.dispatch(END);

  // The `SagaTaskRegistry` keeps track of the promises we have to resolve
  // before we can render
  sagaTaskRegistry.getPromise().then(...)
});

Теперь компонент можно украсить простым хуком:

const hooks = {
  fetch: ({ dispatch }) => {
    dispatch(yourAction());
  },
};

С этого момента вы можете просто использовать саги, как обычно. Это должно дать вам возможность делать то, что вы пытаетесь сделать. Вы можете дополнительно абстрагироваться от этого, чтобы обеспечить динамическую регистрацию саг в фрагментах с разделением кода и другие вещи. Реестр задач уже работает для этих вариантов использования, проверяя новые зарегистрированные задачи с момента последнего вызова getPromise перед фактическим выполнением обещания.

person Johannes Lumpe    schedule 03.01.2017