【问题标题】:Next.js and Redux-Observable. Handle requests in getInitialPropsNext.js 和 Redux-Observable。在 getInitialProps 中处理请求
【发布时间】:2020-04-28 07:22:11
【问题描述】:

我正在寻找示例如何使用 Next.jsgetInitialProps 挂钩来处理 redux-observable ajax 史诗。有没有可能?

换句话说 - 如何在getInitialProps 中使用它:

import { ajax } from 'rxjs/observable/dom/ajax';

// action creators
const fetchUser = username => ({ type: FETCH_USER, payload: username });
const fetchUserFulfilled = payload => ({ type: FETCH_USER_FULFILLED, payload });

// epic
const fetchUserEpic = action$ =>
  action$.ofType(FETCH_USER)
    .mergeMap(action =>
      ajax.getJSON(`https://api.github.com/users/${action.payload}`)
        .map(response => fetchUserFulfilled(response))
    );

...所以我可以在服务器上获取初始数据。

getInitialProps 的文档

【问题讨论】:

    标签: ajax redux rxjs redux-observable nextjs


    【解决方案1】:

    如果我理解正确,您想从getInitialProps 调度操作并确保这些操作已完成,以便可以进行渲染。看看这个库 https://github.com/mquintal/next-redux-observable 它可能会有所帮助。

    【讨论】:

    • 感谢分享!请分享一些关于这个库是如何工作的解释等。否则推荐只是一个评论:)
    【解决方案2】:

    编辑:我最初的回答并没有说得足够清楚,我的意思是通常不可能,也不是惯用的,在组件(或 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

    【讨论】:

    • 感谢您的回答,但不幸的是,这不是我的问题的答案。我知道如何设置我的史诗会听的动作,我的问题是我可以(以某种方式)将它们与 Next.js 一起使用来获取服务器上的初始数据。这是description of static method that does that in Next.js
    • 你在这方面有什么收获吗?我在你现在所在的地方。
    • 我已更新我的答案以使其更清晰,并在需要时包括一种方法。
    猜你喜欢
    • 2019-10-04
    • 2020-06-22
    • 1970-01-01
    • 2019-03-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-12
    • 2021-06-18
    • 1970-01-01
    相关资源
    最近更新 更多