javascript 如何在同构 React + React Router 应用程序中处理 Post 请求

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/31079158/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-28 13:14:51  来源:igfitidea点击:

How to Handle Post Request in Isomorphic React + React Router Application

javascriptreactjsreact-router

提问by Riko Nagatama

I want to build Isomorphic react+ react-routerapplication and after a few days googling, now I can achieve isomorphic application that only handles GET request.

我想构建同构react+react-router应用程序,经过几天的谷歌搜索,现在我可以实现只处理GET请求的同构应用程序。

Here's what I've done so far:

这是我到目前为止所做的:

  1. Server use react-routerto handle all request
  2. react-routerwill call fetchDatafunctions that resides in each React View that matches the route.
  3. Set the data fetched before into props of the React View and render it into string
  4. Inject the stringand data fetched before as global variable window.__STATE__into HTML and deliver the HTML to the client
  5. We have successfully render React App from the server
  6. When the client finished loading our React App javascript, it will try to render. But we pass the state from window.__STATE__as the props of our React App, and React will not re-render because the state is the same
  1. 服务器用于react-router处理所有请求
  2. react-router将调用fetchData驻留在与路由匹配的每个 React 视图中的函数。
  3. 将之前获取的数据设置为 React View 的 props 并将其渲染为 string
  4. string之前获取的数据和数据作为全局变量window.__STATE__注入到 HTML 中,并将 HTML 传递给客户端
  5. 我们已经成功地从服务器渲染了 React App
  6. 当客户端完成加载我们的 React App javascript 时,它会尝试渲染。但是我们将 state fromwindow.__STATE__作为我们 React App 的 props传递,React 不会重新渲染,因为 state 是相同的

The problem is it will not work with POST/PUT/DELETE/WHATEVER request. When handling GET request, react-routerhave information about paramsand query. For example if we have a route: /user/:uidand client request this url: /user/1?foo=bar, then paramswould be: {uid: 1}and querywould be {foo: 'bar'}

问题是它不适用于 POST/PUT/DELETE/WHATEVER 请求。处理 GET 请求时,react-router获取有关params和 的信息query。例如,如果我们有一个 route:/user/:uid并且客户端请求这个 url: /user/1?foo=bar,那么params将是:{uid: 1}并且query将是{foo: 'bar'}

react-routerthen can pass it down to fetchDatafunction so it will know to fetch user with uidof 1 and do whatever with fooquery.

react-router然后可以将它传递给fetchData函数,以便它知道使用uid1获取用户并使用foo查询执行任何操作。

While in POST request, react-routerdoesn't know about the POST parameters. On Server, of course we could pass the POST parameters to fetchDatafunction, but what about the Client? It doesn't know what the POST parameters are.

在 POST 请求中,react-router不知道 POST 参数。在服务器上,当然我们可以将 POST 参数传递给fetchData函数,但是客户端呢?它不知道 POST 参数是什么。

Is there a way that the server could tell the Client about the POST parameters? Below is an example of my Login View. I want when user submit the form, the server will render error message on error, or redirect it to dashboard on success.

有没有办法让服务器可以告诉客户端有关 POST 参数的信息?下面是我的登录视图的示例。我希望当用户提交表单时,服务器会在出错时呈现错误消息,或者在成功时将其重定向到仪表板。

fetchData.js

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

服务器.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

登录.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);
    }
  }
}

回答by Jonny Buchanan

Getting "POST" data on the client

在客户端获取“POST”数据

On the client side, you get POST data by extracting values from your form inputs in a way which corresponds to what you would have received on the server had the form been submitted normally.

在客户端,您通过从表单输入中提取值来获取 POST 数据,这种方式与如果表单正常提交时您在服务器上收到的内容相对应。

Using POST data

使用 POST 数据

So now you have your POST data, but you still have the problem that there's no way to feed the POST data into your transition hooks in React Router 0.13.x and earlier. I created a pull request for this featurewhich has now been closed because it was included as part of the rewrite for the upcoming v1.0 release.

所以现在你有了你的 POST 数据,但你仍然有问题,在 React Router 0.13.x 和更早的版本中,没有办法将 POST 数据提供给你的转换钩子。我为此功能创建了一个拉取请求,该请求现已关闭,因为它已包含在即将发布的 v1.0 版本的重写中。

The gist of it is that locations now have a state object for squireling away any extra data you need about the current request/transition (the two are analagous) being handled:

其要点是,位置现在有一个状态对象,用于收集有关正在处理的当前请求/转换(两者是类似的)所需的任何额外数据:

  • On the server, you're dealing with one request at a time, so you create a static Location with data from req.body
  • On the client you pass the state object (containing extracted form data) to transitionTo().
  • 在服务器上,您一次处理一个请求,因此您使用来自 req.body
  • 在客户端上,您将状态对象(包含提取的表单数据)传递给transitionTo().

Now your transition hook is capable of receiving the same form data in both environments. If things go well, great! If things don't go well, you need a way to pass errors and re-render the form again. New state object to the rescue again! Use transition.redirect()and pass both input data and errors and you now have everything you need to render on both sides.

现在您的转换挂钩能够在两种环境中接收相同的表单数据。如果一切顺利,那就太好了!如果事情进展不顺利,您需要一种方法来传递错误并再次重新呈现表单。新的状态对象再次救援!使用transition.redirect()并传递输入数据和错误,您现在拥有在两侧渲染所需的一切。

I'm not going into more specific detail right now because v1.0 is still in beta and v0.13.x doesn't have the necessary API to do this anyway, but I have a repository which uses the pull request above to implement this workflow with 0.13.x which you could look at in the meantime:

我现在不讨论更具体的细节,因为 v1.0 仍处于测试阶段,而 v0.13.x 无论如何都没有必要的 API 来执行此操作,但我有一个使用上述拉取请求来实现的存储库您可以同时查看带有 0.13.x 的此工作流程:

  • isomorphic-lab- the README gives an overview of how things fit together.

Here are some rough flow diagrams of the process, too:

以下是该过程的一些粗略流程图:

Server POST with errors and redisplay

服务器 POST 有错误并重新显示

Server flow diagram

服务器流程图

Client POST with errors and redisplay

客户端 POST 有错误并重新显示

Client flow diagram

客户端流程图



I've also created a few reusable modules related to this scenario:

我还创建了一些与此场景相关的可重用模块:

  • get-form-datagets data from a form's inputs in the format it would have been POSTed in.
  • react-auto-formprovides <AutoForm>, which you can use instead of <form>to receive all the data from a form's inputs as an argument to its onSubmithandler
  • react-router-form, which is to <form>what React Router's <Link>is to <a>- it handles triggering a transition to the given action, passing methodand body(form data) state - this will be updated for v1.0 soon.
  • get-form-data从表单的输入中获取数据,其格式为 POST 格式。
  • react-auto-form提供<AutoForm>,您可以使用它来代替<form>从表单输入接收所有数据作为其onSubmit处理程序的参数
  • react-router-form,这就是<form>React Router 的<Link>作用<a>——它处理触发到给定action、传递methodbody(表单数据)状态的转换——这将很快更新到 v1.0。