我试着用Redux和Redux-saga对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()
])
}误差
/Users/rahsinwb/Documents/Mine/code/SSR/build/bundle.js:309:39 : TypeError:无法读取未定义的属性“then”
我在这里错过了什么?或者有什么方法可以证明我们可以听到传奇的结局?我在想,组件中用于初始操作调用的loadData助手将返回承诺,但这是行不通的。
LoadData
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))
})现在代码编译时没有任何错误,但问题是saga从loadData方法(即FetchData )发出一个动作,在fetchData调用前面提到的saga中的Save_DATA操作之前,调用被取消了我的假设,还原器是空的,这会导致html不装载数据。
我们如何知道,从启动的操作开始到加载数据的操作,生成器函数的数据加载已经完成?作为仅在服务器上呈现组件的帖子。
Update2
还检查了一个库,但出于一些奇怪的原因,它会引发再生器问题,但我认为不需要库来达到这样的目的,因为我计划重用相同的saga,用于客户端Redux调用,并希望将更改隔离开来。index.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))
)添加Set超时会有所帮助,我可以在服务器中获取数据,然后再呈现,所以现在问题的关键是,我们需要一些方法来等待生成器函数的解决,然后调用呈现。
更新3
增加了一种解决这个问题的方法,但我认为这不是最好的解决方法,而且可以把这个逻辑抽象出来。
发布于 2020-05-10 11:59:25
TypeError:无法读取未定义的属性“那么”
.done getter自v1以来就不再受欢迎了,因此.toPromise()是侦听传奇解析的方法。
至于等待saga完成,有一个特殊的END操作,当它的所有子任务完成后,您可以分派它来终止saga,从而导致store.runSaga(rootSaga).toPromise()承诺解决。
当承诺被解决后(意味着所有必需的数据都已经存在),您应该发送从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);
});此外,您还可以跟踪这个例子。
发布于 2020-05-10 08:57:37
我找到了一种解决这个问题的方法,但这并不是一个干净的解决方案,我也是这样做的--我不想为每个loadData方法+传奇做这件事。我能够把它抽象出来,因为我仍然对这个答案不满意,有一个更干净的方法来实现这个目标吗?
loadData in Component
const loadData = (store) => {
//store.dispatch({type: FETCH_USERS})
return new Promise((resolve) => {
store.dispatch({type: FETCH_USERS, callback: resolve })
});
}Saga
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了
Promise.all(promises).then(data => {
console.log('here',data);
res.send(renderer(req, store))
})是否有一个更清洁的方法来解决这个问题,因为我需要为其他传奇也这样做。
https://stackoverflow.com/questions/61578336
复制相似问题