我想构建同构react
+react-router
应用程序,经过几天的谷歌搜索,现在我可以实现只处理GET请求的同构应用程序。
这是我到目前为止所做的:
- 服务器用于
react-router
处理所有请求 react-router
将调用fetchData
驻留在与路由匹配的每个 React 视图中的函数。- 将之前获取的数据设置为 React View 的 props 并将其渲染为
string
- 将
string
之前获取的数据和数据作为全局变量window.__STATE__
注入到 HTML 中,并将 HTML 传递给客户端 - 我们已经成功地从服务器渲染了 React App
- 当客户端完成加载我们的 React App javascript 时,它会尝试渲染。但是我们将 state from
window.__STATE__
作为我们 React App 的 props传递,React 不会重新渲染,因为 state 是相同的
问题是它不适用于 POST/PUT/DELETE/WHATEVER 请求。处理 GET 请求时,react-router
获取有关params
和 的信息query
。例如,如果我们有一个 route:/user/:uid
并且客户端请求这个 url: /user/1?foo=bar
,那么params
将是:{uid: 1}
并且query
将是{foo: 'bar'}
react-router
然后可以将它传递给fetchData
函数,以便它知道使用uid
1获取用户并使用foo
查询执行任何操作。
在 POST 请求中,react-router
不知道 POST 参数。在服务器上,当然我们可以将 POST 参数传递给fetchData
函数,但是客户端呢?它不知道 POST 参数是什么。
有没有办法让服务器可以告诉客户端有关 POST 参数的信息?下面是我的登录视图的示例。我希望当用户提交表单时,服务器会在出错时呈现错误消息,或者在成功时将其重定向到仪表板。
fetchData.js
import whenKeys from 'when/keys';
export default (authToken, routerState) => {
var promises = routerState.routes.filter((match) => {
return match.handler.fetchData;
}).reduce((promises, match) => {
promises[match.name] = match.handler.fetchData(authToken, routerState.params, routerState.query);
return promises;
}, {});
return whenKeys.all(promises);
}
服务器.js
...
app.use((req, res) => {
const router = Router.create({
routes,
location: req.originalUrl,
onError: next,
onAbort: (abortReason) => {
next(abortReason);
}
});
router.run((Handler, state) => {
fetchData(authToken, state).then((data) => {
// render matched react View and generate the HTML
// ...
})
});
});
...
登录.jsx
import React from 'react';
import DocumentTitle from 'react-document-title';
import api from './api';
export default class Login extends React.Component {
constructor(props) {
super(props);
// how to fill this state with POST parameters on error?
// how to redirect on success?
// and remember that this file will be called both from server and client
this.state = {
error: '',
username: '',
password: ''
};
}
// I saw some people use this function, but it'll only work if
// the form's method is GET
static willTransitionTo(transition, params, query) {
// if only we could read POST parameters here
// we could do something like this
transition.wait(
api.post('/doLogin', postParams).then((data) => {
transition.redirect(`/dashboard`);
});
);
}
render() {
return (
<DocumentTitle title="Login">
<div className="alert alert-danger">{this.state.error}</div>
<form method="post">
<input type="text" name="username" value={this.state.username} onChange={this._onFieldChange('username')} placeholder="Username" /><br />
<input type="password" name="password" value={this.state.password} onChange={this._onFieldChange('password')} placeholder="Password" /><br />
<button type="submit">Login</button>
</form>
</DocumentTitle>
);
}
_onFieldChange(name) {
var self = this;
return (e) => {
e.preventDefault();
var nextState = {};
nextState[name] = e.target.value;
self.setState(nextState);
}
}
}