Как обрабатывать почтовый запрос в изоморфном реакторе + применение реактивного маршрутизатора

Я хочу создать приложение 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, который вы могли бы посмотреть в это время:

  • isomorphic-lab - README дает общий обзор того, как все сочетается.

Вот некоторые грубые диаграммы процесса:

Сервер 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.