如何使用 redux-saga 在 getInitialProps 中获取数据。然后在 getInitialProps 方法中从 redux store 获取响应?
Posted
技术标签:
【中文标题】如何使用 redux-saga 在 getInitialProps 中获取数据。然后在 getInitialProps 方法中从 redux store 获取响应?【英文标题】:How to fetch data in getInitialProps with redux-saga.Then get the response from redux store while in the getInitialProps method? 【发布时间】:2020-06-04 05:24:49 【问题描述】:我完成了使用 create-react-app (CSR) 创建的 React 应用程序的编码,但我现在正在使用 Next.js 框架重写整个应用程序以获得更好的 SEO 性能。
在重写它时,我很难弄清楚如何正确处理 redux 和 redux-saga 来执行获取和存储数据的过程。 在这个项目中使用 Next.js 的主要原因是利用 getInitialProps 方法,在第一个页面加载发生之前在服务器端获取数据。但由于某种原因,我无法“等待” Redux 调度完成并按时获取获取的数据。
所以最终发生的事情是我分派了操作以获取数据,但在初始服务器端页面加载期间它没有按时存储在 redux 存储中。但是当我使用 next/link 更改路由时,数据会进来,但仅在客户端,在服务器端渲染发生之后。
所以它有点违背了使用 Next.js 的目的。
这个新代码与 create-react-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/reducers/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' );
正如你所见,这个应用程序是一个非常普通的 react redux-saga 应用程序。除了使用 redux-saga 从后端获取数据之外,其他一切都在正常工作。
有没有办法让 getInitialProps 方法按预期与 redux 和 redux-saga 一起工作?
【问题讨论】:
您找到了可行的解决方案吗? 【参考方案1】:查看官方 Next.js 示例和 redux-saga 示例。
https://github.com/zeit/next.js/tree/canary/examples/with-redux-saga
【讨论】:
以上是关于如何使用 redux-saga 在 getInitialProps 中获取数据。然后在 getInitialProps 方法中从 redux store 获取响应?的主要内容,如果未能解决你的问题,请参考以下文章
无法弄清楚如何使用 redux-saga-test-plan 测试 redux-saga 功能
如何使用 Redux-Saga 和 Hooks 调用 API
如何将 redux-sagas 与 react-hooks 一起使用
如何使用 redux-saga 在 getInitialProps 中获取数据。然后在 getInitialProps 方法中从 redux store 获取响应?