javascript - 我们如何从 Redux - saga 中的 store.dispatch 返回一个 Promise,以便我们可以等待解析然后在 SSR 中渲染?

标签 javascript reactjs redux redux-saga

我正在尝试使用 Redux 和 Redux-saga 来 React SSR。

我能够让客户端渲染工作,但服务器存储似乎永远不会在渲染 HTML 之前获取数据/等待数据。

server.js

import express from 'express';
import renderer from "./helpers/renderer";
import createStore from "./helpers/createStore";
import {matchRoutes} from 'react-router-config';
import Routes from "./client/Routes";
import rootSaga from "./client/saga";


const app = express();

app.use(express.static('public'));
app.get('*', (req, res) => {
    const store = createStore();
    const branch = matchRoutes(Routes, req.path);
    const promises = branch.map(({ route }) => {
        return route.loadData ? route.loadData(store) : Promise.resolve(null);
    });
    store.runSaga(rootSaga).done.then(() => {
        res.send(renderer(req, store)) // helper method to load static router and provider  
    }) // Not required after Update 2 , Promise.All should work .. 
});

app.listen(3000, () => {
    console.log('Listening on Port 3000');
})

小传奇

import { put, takeEvery, all, call } from 'redux-saga/effects'
import {FETCH_USERS, SAVE_USERS} from "../actions";
import axios from 'axios';

export function* fetchUsers() {
    const res = yield call(getUsers);
    yield put({ type: SAVE_USERS, payload: res.data});
}


const getUsers = () => {
    const response = axios.get('http://react-ssr-api.herokuapp.com/users');
    return response;
}

export function* actionWatcher() {
    yield takeEvery(FETCH_USERS, fetchUsers)
}

export default function* rootSaga() {
    yield all([
        actionWatcher()
    ])
}

错误

TypeError: Cannot read property 'then' of undefined at /Users/rahsinwb/Documents/Mine/code/SSR/build/bundle.js:309:39

我在这里缺少什么?或者有什么行之有效的方法可以让我们听到传奇的结局吗?我认为组件中带有 store.dispatch 的初始操作调用的 loadData 助手将返回 Promise,但这永远不会起作用。

加载数据

const loadData = (store) => {
    store.dispatch({type: FETCH_USERS});
}

更新

将此添加到我的 index.js 文件

 store.runSaga(rootSaga).toPromise().then(() => {
        branch.map(({ route }) => {
            return route.loadData ? route.loadData(store) : Promise.resolve(null);
        });
        res.send(renderer(req, store))
    })

现在代码编译没有任何错误,但问题是传奇从 loadData 方法分派(dispatch)一个操作,即 FetchData,在 fetchData 调用上述传奇中的 Save_DATA 操作之前,调用被取消了我的假设,并且 reducer 留空这会导致 html 无法加载数据。

How do we know that the data load is completed for the generator function starting from the action initiated to the action for loading data ? to the reducer as only post that i should render component on the server

更新2

还检查了lib ,但由于某些奇怪的原因,它引发了再生器问题,但我不认为出于这样的目的需要库,因为我计划为客户端 Redux 调用重用相同的传奇,并希望将更改分开。 索引.js

 console.log(promises); // no promise returned  undefined
   //setTimeout((function() {res.send(renderer(req, store))}), 2000);
   Promise.all(promises).then(
      res.send(renderer(req, store))
   )

Adding Set Timeout helps and i am able to fetch data in server and then render so now the CRUX of the problem is that we need to some how wait for the generator function to resolve then call render.

更新3

添加了解决这个问题的方法作为答案,但我不认为这是最好的解决方案,并且可以抽象出这个逻辑

最佳答案

TypeError: Cannot read property 'then' of undefined at

.done getter 自 v1 起已被弃用,因此 .toPromise() 是监听 saga 解析的方法

对于等待 sagas 完成,有一个特殊的 END 操作,您可以在其所有子任务完成时调度该操作来终止 saga,从而导致 store.runSaga(rootSaga).toPromise () promise 解决。

当 promise 得到解决(意味着所有必需的数据都已存在)时,您应该发送从 renderToString 收到的结果 HTML 作为响应。请注意,服务器渲染的组件并未真正安装,因此 componentDidMount() 方法不会被触发,因此您的数据不会被提取两次。

因此您可以按如下方式修改代码:

// server.js
...
import { END } from 'redux-saga';

app.get('*', (req, res) => {
    const store = createStore();
    const branch = matchRoutes(Routes, req.path);

    store.runSaga(rootSaga).toPromise().then(() => {
        res.send(renderer(req, store))
    });

    // trigger data fetching
    branch.forEach(({ route }) => {
        route.loadData && route.loadData(store)
    });

    // terminate saga
    store.dispatch(END);
});

此外,您还可以关注this example .

关于javascript - 我们如何从 Redux - saga 中的 store.dispatch 返回一个 Promise,以便我们可以等待解析然后在 SSR 中渲染?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61578336/

相关文章:

javascript - 如何从服务器端补充我的 Apollo 状态?

javascript ctypes 字符串转无符号字符

javascript - 使用 jquery 在 AngularJS 中仅选择一个子元素

node.js - 我可以在 Node 中要求 jsx 文件吗?

json - Superagent:错误:解析器无法解析响应

javascript - 如何执行对 REST api 的异步 axios 调用并更新商店(redux thunk 令人困惑)?

javascript - 无论 Controller 如何,Angularjs 在每个 routeChange 之前运行一个 XHR 请求

javascript - 使用 CasperJS 选择下拉选项

javascript - React Hooks,渲染之间的 prop 值随机变化

javascript - 路由不适用于 React router v4