ReactJS:通过表单获取和更新数据的惯用方式是什么?
ReactJS: What is the idiomatic way to fetch and update data through a form?
我正在努力思考通过 HTML 表单更新数据的最佳方式。以 ReactJS 网页为例:
https://facebook.github.io/react/docs/tutorial.html
假设您想要实现用户编辑她过去发布的评论的功能(即不在当前会话中,因此必须从服务器获取)。编辑评论页面需要预先填写评论的现有作者姓名和文本。您将如何实现获取评论数据并预填写评论表单?以下是我脑子里的矛盾想法,我无法理清(让我们称新组件为CommentEdit
):
- 最初的评论作者和文本应该是
CommentEdit
的道具,因为它们不是状态
CommentEdit
组件应该是可重用的,因此它应该能够从服务器本身获取初始数据,但随后必须将其保存为状态
- 如果
CommentEdit
的父级要获取并设置 CommentEdit
的道具,它必须将其保存为状态,所以那里没有太多节省
- Flux 之类的东西可能有用,但 Flux 更让我困惑。如果 Flux 要存储评论,它是否必须为用户保存每条评论?如果用户有数千条评论会怎样?
老实说,我只是希望组件可以改变自己的道具。看起来它会使组件更可重用。
Honestly, I just wish components could change their own props. Seems like it would make components a lot more reusable.
并非如此,可以更改自己的 props 的组件并不比那些不能更改的组件更可重用,只是它们的底层实现会变得更加复杂。
当前的 react.js
数据流和事件冒泡使组件 "very" 具有确定性,因为在组件生命周期中的任何时候,您都知道组件将根据您的输入生成什么输出给它。这在调试和单元测试应用程序时也有很大帮助。
现在,让您进退两难的是,CommentEdit
只需要 content
属性,因为作者不会更改,并且永远是登录该网站的人。其他状态不需要,因为编辑的内容会在用户点击save
按钮时冒泡,当comments
列表存储在state
的时候会被插入到列表中组件祖先之一将被更新。
如果您为每个评论正确设置 key
属性,那么 React 会很好地注意到编辑评论的 html 元素不需要重新创建,只有呈现评论的 html 元素的 innerHTML
需要更改。
如果您想要显示作者,您可以将其添加为道具,它不会 affect/improve 组件本身,除非您想使用此布局构建多个站点。
Flux 是您可以用来从应用程序中获取数据的最佳工具,您不必在组件内部使用状态,使用 props 来收取初始数据,然后使用 react.DOMNode 来获取来自您需要的表单的每个元素的数据 https://facebook.github.io/react/docs/top-level-api.html#react.finddomnode ,还在所有表单组件中使用 refs,最后您可以使用 flux 将数据发送到服务器并保存它。
我最终通过向我的每个组件添加 "wrapper" parent 来解决这个问题。包装器组件负责从服务器获取数据,并将其存储在其状态中。包装器会将获取的状态作为道具传递给它的 children。然后我将这个设置抽象为 mixin,创建一个包装器 mixin 和包装器 child mixin,这样我就可以 re-use 轻松地进行这个设置。
此模式非常适合您应用中的典型表单。包装器将获取数据到 pre-populate 表单,而 child 将表单发送到服务器并将负责 error-handling.
这是一个例子:
// This handles the form submission and error handling
var EditFormMixin = {
getInitialState: function() {
return {formState:FORM_STATE_EDITING, errors:{}}
},
handleSubmitBase: function(event, url, data, method, success, error) {
event.preventDefault();
this.setState({formState:FORM_STATE_SUBMITTING, errors:{}});
$.ajax({
url: url,
dataType: "json",
contentType: "application/json; charset=utf-8",
data: JSON.stringify(data),
method: method,
success: function(data) {
this.setState({formState:FORM_STATE_SUBMITTED});
if (success) {
success();
};
}.bind(this),
error: function(xhr, status, err) {
this.setState({formState:FORM_STATE_EDITING});
if (xhr.status == 400) {
this.setState({errors:xhr.responseJSON});
}
if (error) {
error();
}
}.bind(this)
});
}
};
// This handles fetching the initial data for the form
var EditFormWrapperMixin = {
getInitialState: function() {
return {data: {}, loadState:LOAD_STATE_UNLOADED, url:this.getUrl()};
},
fetch: function() {
this.setState({loadState:LOAD_STATE_LOADING})
$.ajax({
url: this.state.url,
dataType: "json",
method: "GET",
success: function(data) {
this.setState({data:data, loadState:LOAD_STATE_LOADED});
}.bind(this),
error: function(xhr, status, err) {
}.bind(this)
});
}
componentDidMount: function() {
this.fetch();
},
componentWillReceiveProps: function(nextProps) {
var url = this.getUrl();
this.setState({url:url}, function() {
this.fetch();
});
}
};
我正在努力思考通过 HTML 表单更新数据的最佳方式。以 ReactJS 网页为例:
https://facebook.github.io/react/docs/tutorial.html
假设您想要实现用户编辑她过去发布的评论的功能(即不在当前会话中,因此必须从服务器获取)。编辑评论页面需要预先填写评论的现有作者姓名和文本。您将如何实现获取评论数据并预填写评论表单?以下是我脑子里的矛盾想法,我无法理清(让我们称新组件为CommentEdit
):
- 最初的评论作者和文本应该是
CommentEdit
的道具,因为它们不是状态 CommentEdit
组件应该是可重用的,因此它应该能够从服务器本身获取初始数据,但随后必须将其保存为状态- 如果
CommentEdit
的父级要获取并设置CommentEdit
的道具,它必须将其保存为状态,所以那里没有太多节省 - Flux 之类的东西可能有用,但 Flux 更让我困惑。如果 Flux 要存储评论,它是否必须为用户保存每条评论?如果用户有数千条评论会怎样?
老实说,我只是希望组件可以改变自己的道具。看起来它会使组件更可重用。
Honestly, I just wish components could change their own props. Seems like it would make components a lot more reusable.
并非如此,可以更改自己的 props 的组件并不比那些不能更改的组件更可重用,只是它们的底层实现会变得更加复杂。
当前的 react.js
数据流和事件冒泡使组件 "very" 具有确定性,因为在组件生命周期中的任何时候,您都知道组件将根据您的输入生成什么输出给它。这在调试和单元测试应用程序时也有很大帮助。
现在,让您进退两难的是,CommentEdit
只需要 content
属性,因为作者不会更改,并且永远是登录该网站的人。其他状态不需要,因为编辑的内容会在用户点击save
按钮时冒泡,当comments
列表存储在state
的时候会被插入到列表中组件祖先之一将被更新。
如果您为每个评论正确设置 key
属性,那么 React 会很好地注意到编辑评论的 html 元素不需要重新创建,只有呈现评论的 html 元素的 innerHTML
需要更改。
如果您想要显示作者,您可以将其添加为道具,它不会 affect/improve 组件本身,除非您想使用此布局构建多个站点。
Flux 是您可以用来从应用程序中获取数据的最佳工具,您不必在组件内部使用状态,使用 props 来收取初始数据,然后使用 react.DOMNode 来获取来自您需要的表单的每个元素的数据 https://facebook.github.io/react/docs/top-level-api.html#react.finddomnode ,还在所有表单组件中使用 refs,最后您可以使用 flux 将数据发送到服务器并保存它。
我最终通过向我的每个组件添加 "wrapper" parent 来解决这个问题。包装器组件负责从服务器获取数据,并将其存储在其状态中。包装器会将获取的状态作为道具传递给它的 children。然后我将这个设置抽象为 mixin,创建一个包装器 mixin 和包装器 child mixin,这样我就可以 re-use 轻松地进行这个设置。
此模式非常适合您应用中的典型表单。包装器将获取数据到 pre-populate 表单,而 child 将表单发送到服务器并将负责 error-handling.
这是一个例子:
// This handles the form submission and error handling
var EditFormMixin = {
getInitialState: function() {
return {formState:FORM_STATE_EDITING, errors:{}}
},
handleSubmitBase: function(event, url, data, method, success, error) {
event.preventDefault();
this.setState({formState:FORM_STATE_SUBMITTING, errors:{}});
$.ajax({
url: url,
dataType: "json",
contentType: "application/json; charset=utf-8",
data: JSON.stringify(data),
method: method,
success: function(data) {
this.setState({formState:FORM_STATE_SUBMITTED});
if (success) {
success();
};
}.bind(this),
error: function(xhr, status, err) {
this.setState({formState:FORM_STATE_EDITING});
if (xhr.status == 400) {
this.setState({errors:xhr.responseJSON});
}
if (error) {
error();
}
}.bind(this)
});
}
};
// This handles fetching the initial data for the form
var EditFormWrapperMixin = {
getInitialState: function() {
return {data: {}, loadState:LOAD_STATE_UNLOADED, url:this.getUrl()};
},
fetch: function() {
this.setState({loadState:LOAD_STATE_LOADING})
$.ajax({
url: this.state.url,
dataType: "json",
method: "GET",
success: function(data) {
this.setState({data:data, loadState:LOAD_STATE_LOADED});
}.bind(this),
error: function(xhr, status, err) {
}.bind(this)
});
}
componentDidMount: function() {
this.fetch();
},
componentWillReceiveProps: function(nextProps) {
var url = this.getUrl();
this.setState({url:url}, function() {
this.fetch();
});
}
};