编辑:我最初的回答并没有说得足够清楚,我的意思是通常不可能,也不是惯用的,在组件(或 getInitialProps)内调度动作并提供完成时通知某种回调。
相反,通常你会派发一个动作,你的 Epic 会处理它(执行副作用),然后他们自己会派发其他动作来触发 Redux 中的状态更改。您的组件将仅根据 Redux 存储的状态更改重新渲染。这就是它的设计方式,无论好坏。
对于那些因此而被否决的人,我很抱歉,这不是 Redux Observable 的设计初衷。 Redux Observable 在设计时并未考虑到 Next.js。如果您使用它和 Redux 只是作为获取数据的一种方式,那么它可能比它的价值更多的仪式,而不是仅仅调用 fetch()。
也就是说,软件几乎总是具有延展性的,呵呵。在技术上可能让某些东西发挥作用,即使这不是 RO 的意图。不过,我不推荐它。 RO 并非用于提前页面渲染之类的事情,而是用于复杂的客户端交互。你当然可以两者都做!在 getInitialProps 中使用常规的 fetch() 或其他 AJAX 实用程序,然后根据 RO 来满足复杂的客户端要求(如果适用)。
不管怎样,现在Next.js repo of how someone else 中有一个例子接近它,但我个人并不提倡这种做法。
如果我必须这样做,这是我的:
/* 1. Add something like this to your root reducer
*************************************************/
function lastActionType(state = null, action) {
// If you wanted to, you could store the entire action including the payload,
// but if you ever have actions which have a large payload, it would prevent
// it from being garbage collected until the next action was dispatched.
return action.type;
}
const rootReducer = combineReducers({
lastActionType, // <-- add this
...otherReducers,
});
/* 2. Create a utility to listen for an action
*************************************************/
function waitForAction(store, options) {
return new Promise((resolve, reject) => {
const unsubscribe = store.subscribe(() => {
const state = store.getState();
if (state.lastActionType === options.fulfilled.type) {
unsubscribe();
const value = options.fulfilled.selector(state);
resolve(value);
} else if (state.lastActionType === options.rejected.type) {
unsubscribe();
const value = options.rejected.selector(state);
reject(value);
}
});
});
}
/* 3. Use the utility
*************************************************/
async function getInitialProps() {
// Kick it off
store.dispatch(fetchUser('user123'));
try {
// Wait for it
const user = await waitForAction(store, {
fulfilled: {
type: FETCH_USER_FULFILLED,
selector: (state) => {
// Some sort of state selector for looking up users
return state.user;
},
},
// If you want to have error handling, this might be how you'd do it too.
rejected: {
type: FETCH_USER_REJECTED,
selector: (state) => {
// Some sort of state selector for errors
return state.userError;
},
},
});
return {
user,
...otherPropsYouHave,
};
} catch (e) {
// Handle errors in some way
}
}
要将 redux-observable 与 React 一起使用,您的 UI 组件将调度简单的操作,然后您的 Epic 将监听并响应这些操作。因此,您的组件对 redux-observable 没有直接的了解——它们只是调度操作并连接到存储以接收状态更改。
这是来自文档的一个简单 JSBin 示例:https://jsbin.com/jexomi/edit?js,output
const App = ({ isPinging, ping }) => (
<div>
<h1>is pinging: {isPinging.toString()}</h1>
<button onClick={ping}>Start PING</button>
</div>
);
export default connect(
({ isPinging }) => ({ isPinging }),
{ ping }
)(App);
这里还有一个稍微复杂一点的例子:https://github.com/redux-observable/redux-observable/tree/master/examples/redux-observable-shopping-cart