首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >我们如何返回一个承诺,从一个store.dispatch在Redux -佐贺,以便我们可以等待的决心,然后在SSR渲染?

我们如何返回一个承诺,从一个store.dispatch在Redux -佐贺,以便我们可以等待的决心,然后在SSR渲染?
EN

Stack Overflow用户
提问于 2020-05-03 16:56:26
回答 2查看 2.1K关注 0票数 5

我试着用Redux和Redux-saga对SSR做出反应。

我能够让客户端呈现工作,但服务器存储似乎从来没有得到数据/等待数据才能呈现HTML。

server.js

代码语言:javascript
复制
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');
})

小传奇

代码语言:javascript
复制
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()
    ])
}

误差

/Users/rahsinwb/Documents/Mine/code/SSR/build/bundle.js:309:39 : TypeError:无法读取未定义的属性“then”

我在这里错过了什么?或者有什么方法可以证明我们可以听到传奇的结局?我在想,组件中用于初始操作调用的loadData助手将返回承诺,但这是行不通的。

LoadData

代码语言:javascript
复制
const loadData = (store) => {
    store.dispatch({type: FETCH_USERS});
}

更新

把这个添加到我的index.js文件中

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

现在代码编译时没有任何错误,但问题是saga从loadData方法(即FetchData )发出一个动作,在fetchData调用前面提到的saga中的Save_DATA操作之前,调用被取消了我的假设,还原器是空的,这会导致html不装载数据。

我们如何知道,从启动的操作开始到加载数据的操作,生成器函数的数据加载已经完成?作为仅在服务器上呈现组件的帖子。

Update2

还检查了一个,但出于一些奇怪的原因,它会引发再生器问题,但我认为不需要库来达到这样的目的,因为我计划重用相同的saga,用于客户端Redux调用,并希望将更改隔离开来。index.js

代码语言:javascript
复制
 console.log(promises); // no promise returned  undefined
   //setTimeout((function() {res.send(renderer(req, store))}), 2000);
   Promise.all(promises).then(
      res.send(renderer(req, store))
   )

添加Set超时会有所帮助,我可以在服务器中获取数据,然后再呈现,所以现在问题的关键是,我们需要一些方法来等待生成器函数的解决,然后调用呈现。

更新3

增加了一种解决这个问题的方法,但我认为这不是最好的解决方法,而且可以把这个逻辑抽象出来。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2020-05-10 11:59:25

TypeError:无法读取未定义的属性“那么”

.done getter自v1以来就不再受欢迎了,因此.toPromise()是侦听传奇解析的方法。

至于等待saga完成,有一个特殊的END操作,当它的所有子任务完成后,您可以分派它来终止saga,从而导致store.runSaga(rootSaga).toPromise()承诺解决。

当承诺被解决后(意味着所有必需的数据都已经存在),您应该发送从renderToString接收的结果HTML作为响应。注意,服务器呈现的组件并没有真正挂载,因此不会触发componentDidMount()方法,因此您的数据不会被抓取两次。

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

代码语言:javascript
复制
// 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);
});

此外,您还可以跟踪这个例子

票数 3
EN

Stack Overflow用户

发布于 2020-05-10 08:57:37

我找到了一种解决这个问题的方法,但这并不是一个干净的解决方案,我也是这样做的--我不想为每个loadData方法+传奇做这件事。我能够把它抽象出来,因为我仍然对这个答案不满意,有一个更干净的方法来实现这个目标吗?

loadData in Component

代码语言:javascript
复制
const loadData = (store) => {
    //store.dispatch({type: FETCH_USERS})
    return new Promise((resolve) => {
        store.dispatch({type: FETCH_USERS, callback: resolve })
    });
}

Saga

代码语言:javascript
复制
function asyncCallBackUtil(action){
    if (action.callback) {
        action.callback('Done');
    }
}

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

现在我可以像在邮报上提到的那样使用Promise.All了

代码语言:javascript
复制
Promise.all(promises).then(data => {
    console.log('here',data);
    res.send(renderer(req, store))
})

是否有一个更清洁的方法来解决这个问题,因为我需要为其他传奇也这样做。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/61578336

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档