使用redux-saga和重拨服务器端

时间:2016-05-01 01:30:15

标签: reactjs isomorphic-javascript redux-saga

现在,我正试图通过Redial获取应用服务器端的初始状态。

重拨会触发纯对象操作,而redux-saga会侦听/等待该操作,然后启动异步请求。

但问题是,Redial没有承诺在redux-saga完成时解决,因为它正在派遣一个纯粹的对象。

组件

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

2 个答案:

答案 0 :(得分:1)

我认为可以这样做:

首先,您需要在本地添加商店。 (代码取自重读自述文件)

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

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

然后你可以像这样手动创建一个Promise:

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 }),
       }
   }
};

这些代码仅用于演示,如果没有经过适当的测试,请不要在生产中使用它们。

我认为可能有一种更优雅的方式来监视saga执行结果,而不是一遍又一遍地检查redux存储状态,直到状态匹配那些if(...)语句,也许你可以运行带有redux存储和外部监听器的传奇,然后那些重拨钩子不需要知道你的商店结构。

答案 1 :(得分:0)

有一种相当优雅的方式。首先,您需要为saga任务创建一个注册表(请记住,运行中间件的.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;
    });
  }
}

使用.run向saga中间件添加新任务时,您将调用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());
  },
};

从现在开始,你可以照常使用传奇。这应该让你有能力做你正在尝试的事情。您可以进一步抽象这个以允许跨代码拆分块和其他东西动态注册sagas。任务注册表已经适用于这些用例,因为在实际解析承诺之前,自上次调用getPromise以来检查新注册的任务。