现在,我正试图通过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
?
答案 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
以来检查新注册的任务。