Как получить данные в getInitialProps с помощью redux-saga. Затем получить ответ из хранилища redux, находясь в методе getInitialProps? - PullRequest
0 голосов
/ 20 февраля 2020

Я закончил кодировать приложение React, созданное с помощью приложения create-реагировать (CSR), но сейчас я переписываю все это приложение, используя инфраструктуру Next. js для повышения производительности SEO.

При переписывании мне пришлось нелегко понять, как правильно обращаться с redux и redux-saga для выполнения процесса извлечения и хранения данных. Основная причина использования Next. js в этом проекте - использование метода getInitialProps для извлечения данных на стороне сервера до загрузки первой страницы. Но по какой-то причине я не могу «дождаться» завершения отправки Redux и своевременного получения выбранных данных.

В итоге происходит то, что я отправляю действие для извлечения данных, но он не хранится в хранилище резервов вовремя во время начальной загрузки страницы на стороне сервера. Но когда я изменяю маршруты, используя next / link, данные поступают, но только на стороне клиента, после того, как на сервере произошел рендеринг.

Так что это своего рода побеждает цель использования Next. js.

Этот новый код очень похож на проект create-Reaction-app, с некоторыми незначительными изменениями, чтобы соответствовать требованиям проекта Next. js.

Вот мой код.

. / Pages / _app. js

import App from 'next/app';
import { Provider } from 'react-redux';
import withRedux from 'next-redux-wrapper';
import withReduxSaga from 'next-redux-saga';
import makeStore from '../store/index';

class MyApp extends App {
  static async getInitialProps({ Component, ctx }) {
    const pageProps = Component.getInitialProps
      ? await Component.getInitialProps(ctx)
      : {};
    return { pageProps };
  }

  render() {
    const { Component, pageProps, store } = this.props;
    return (
      <Provider store={store}>
        <Component {...pageProps} />
      </Provider>
    );
  }
}

export default withRedux(makeStore)(MyApp);

. / Pages / index.jsx:

import React from 'react';
import { useRouter } from 'next/router';

import Layout from '../components/Layout';
import SubNavBarCategories from '../components/Pages/Home/SubNavBarCategory';

import * as BlogActions from '../store/actions/blog/categories';


const Blog = (props) => {
  const router = useRouter();
  const {
    blogCategories,
  } = props;


  return (
    <Layout>
      <SubNavBarCategories blogCategories={blogCategories} />
    </Layout>
  );
};


Blog.getInitialProps = async ({ isServer, store }) => {
  await store.execSagaTasks(isServer, (dispatch) => {
    dispatch(BlogActions.getRecentCategories(5));
  });

  console.log('await store:', await store.getState().blog.blogCategories);
  //output: await store: { data: [], loading: true, fetched: false, error: false }
  //expected something like this: 
  // await store: { data: ['test1', 'category', 'crypto', 'test4', 'Day trade'] loading: false, fetched: true, error: false }

  return {
    blogCategories: await store.getState().blog.blogCategories,
  };
};

export default Blog;

. / Store / index. js

import {
  createStore,
  applyMiddleware,
  compose,
} from 'redux';
import createSagaMiddleware, { END } from 'redux-saga';

import rootReducer from './reducers';
import rootSaga from './sagas';

const sagaMiddleware = createSagaMiddleware();
const makeStore = (initialState) => {
  const composeEnhancers = (typeof window !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) || compose;

  const store = createStore(
    rootReducer,
    initialState,
    compose(
      composeEnhancers(applyMiddleware(sagaMiddleware)),
    ),
  );

  store.runSaga = () => {
    if (store.saga) {
      return;
    }
    store.sagaTask = sagaMiddleware.run(rootSaga);
  };


  store.stopSaga = async () => {
    if (!store.saga) {
      return;
    }
    store.dispatch(END);
    await store.saga.done;
    store.saga = null;
  };

  store.execSagaTasks = async (isServer, tasks) => {
    store.runSaga();
    tasks(store.dispatch);

    await store.stopSaga();

    if (!isServer) {
      store.runSaga();
    }
  };

  store.runSaga();
  return store;
};

export default makeStore;

. / Store / actions / blog / blog. js

export function getRecentCategories(number) {
  return {
    type: 'REQUEST_RECENT_CATEGORIES',
    payload: {
      number,
    },
  };
}

. / Store / redurs / blog / blog. js

import update from 'immutability-helper';

const initialState = {
  blogCategories: {
    data: [],
    loading: false,
    fetched: false,
    error: false,
  },
};

export default function blog(state = initialState, action) {
  switch (action.type) {
    case 'REQUEST_RECENT_CATEGORIES':
      return update(state, {
        blogCategories: {
          loading: { $set: true },
        },
      });
    case 'SUCCESS_RECENT_CATEGORIES':
      console.log('actions:', action.payload.data);
      //output: actions: blogCategories [ 'test1', 'category', 'crypto', 'test4', 'Day trade' ]
      return update(state, {
        blogCategories: {
          data: { $set: action.payload.data },
          loading: { $set: false },
          fetched: { $set: true },
          error: { $set: false },
        },
      });
    case 'FAILURE_RECENT_CATEGORIES':
      return update(state, {
        blogCategories: {
          fetched: { $set: true },
          error: { $set: true },
        },
      });
    default:
      return state;
  }
}

./store/sagas/blog/getRecentCategories.js

import {
  put,
  call,
} from 'redux-saga/effects';

import 'isomorphic-fetch';

async function getRecentCategoriesApi(number) {
  const res = await fetch(`http://localhost:5000/blog/get/categories/newest/${number}`, {
    method: 'GET',
    mode: 'cors',
    cache: 'no-cache',
    credentials: 'same-origin',
    headers: {
      'Content-Type': 'application/json',
    },
  });
  const data = await res.json();
  return data;
}

export default function* asyncGetRecentCategoriesApi(action) {
  try {
    const response = yield call(getRecentCategoriesApi, action.payload.number);

    yield put({ type: 'SUCCESS_RECENT_CATEGORIES', payload: { data: response } });
  } catch (err) {
    yield put({ type: 'FAILURE_RECENT_CATEGORIES' });
  }
}

Как вы можете видеть, это приложение представляет собой довольно простое приложение с реакцией редукса-саги. Все остальное работает так, как должно, кроме получения данных из бэкэнда с использованием метода redux-saga.

Есть ли способ заставить метод getInitialProps работать с redux и redux-saga, как предполагалось?

1 Ответ

1 голос
/ 20 февраля 2020

Посмотрите на официальный пример Next. js с примером redux-saga.

https://github.com/zeit/next.js/tree/canary/examples/with-redux-saga

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