Как обрабатывать почтовый запрос в изоморфном реакторе + применение реактивного маршрутизатора
Я хочу создать приложение Isomorphic react
+ react-router
и через несколько дней googling, теперь я могу получить изоморфное приложение, которое обрабатывает только запрос GET.
Вот что я сделал до сих пор:
- Использование сервера
react-router
для обработки всего запроса
-
react-router
вызовет функции fetchData
, которые находятся в каждом представлении React View, которое соответствует маршруту.
- Установите данные, извлеченные ранее, в реквизиты React View и сделайте их в
string
- Ввести
string
и данные, полученные до глобальной переменной window.__STATE__
, в HTML и доставить HTML-код клиенту
- Мы успешно выполнили приложение React с сервера
- Когда клиент завершит загрузку JavaScript-приложения React App, он попытается выполнить рендеринг. Но мы передаем состояние из
window.__STATE__
в качестве опоры нашего приложения React, а React не будет повторно отображаться, потому что состояние такое же
Проблема заключается в том, что он не будет работать с запросом POST/PUT/DELETE/WHATEVER. При обработке запроса GET react-router
есть информация о params
и query
. Например, если у нас есть маршрут: /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);
}
server.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
// ...
})
});
});
...
login.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 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);
}
}
}
Ответы
Ответ 1
Получение данных "POST" на клиенте
На стороне клиента вы получаете данные POST путем извлечения значений из входов формы таким образом, который соответствует тому, что вы получили на сервере, если бы форма была отправлена в обычном режиме.
Использование данных POST
Итак, теперь у вас есть данные POST, но у вас все еще есть проблема, что нет способа передать данные POST в ваши переходные перехватчики в React Router 0.13.x и ранее. Я создал запрос pull для этой функции, который был закрыт, поскольку он был включен как часть перезаписи для предстоящей версии v1.0.
Суть его в том, что у местоположений теперь есть объект состояния, который отсылает любые дополнительные данные, которые вам нужны в текущем запросе/переходе (два из них являются аналогами):
- На сервере вы имеете дело с одним запросом за раз, поэтому вы создаете статическое местоположение с данными из
req.body
- На клиенте вы передаете объект состояния (содержащий извлеченные данные формы) в
transitionTo()
.
Теперь ваш переходный крючок способен получать одни и те же данные формы в обеих средах. Если все будет хорошо, здорово! Если все идет не так, вам нужно пройти ошибки и повторно отобразить форму снова. Новый объект состояния для спасения снова! Используйте transition.redirect()
и передайте как входные данные, так и ошибки, и теперь у вас есть все необходимое для рендеринга с обеих сторон.
Теперь я не буду вдаваться в более конкретные детали, потому что v1.0 все еще находится в бета-версии, а v0.13.x не имеет необходимого API для этого в любом случае, но у меня есть репозиторий, который использует запрос на pull выше, чтобы реализовать этот рабочий процесс с 0.13.x, который вы могли бы посмотреть в это время:
Вот некоторые грубые диаграммы процесса:
Сервер POST с ошибками и повторным отображением
![Server flow diagram]()
Клиентский POST с ошибками и повторным отображением
![Client flow diagram]()
Я также создал несколько повторно используемых модулей, связанных с этим сценарием:
- get-form-data получает данные из входов формы в формате, в который он был бы отправлен.
- react-auto-form предоставляет
<AutoForm>
, который вы можете использовать вместо <form>
для получения всех данных из входов формы в качестве аргумента в свой onSubmit
обработчик
- react-router-form, который находится на
<form>
, что React Router <Link>
соответствует <a>
- он обрабатывает запуск перехода к заданному action
, передавая состояние method
и body
(данные формы) - это скоро будет обновлено для версии 1.0.