添加新的评论
现在开始创建表单,我们的CommentForm
组件会需要用户填写他们的姓名和评论内容并发送一个请求到服务器保存评论。
修改CommentForm.tsx 如下:
///export =CommentForm;class CommentForm extends React.Component { render() { return ( ); }}
控制组件
使用传统DOM,渲染 input
元素并由浏览器管理状态(它渲染的值)。因此,实际DOM的状态会跟组件的状态不同。视图的状态同组件不同是不理想的。在React中,组件总是代表着视图的状态,不仅仅在初始化的时候。
this.state
在用户键入时保存用户的输入。我们给初始 state
定义了两个属性 author
和 text
并设置成空字符串。在我们的 <input>
元素,我们设置 value
prop 反应组件的 state
并附上 onChange
处理它们。 这些 <input>
元素带有 value
设置的我们叫它们为控制组件。 ///export =CommentForm;interface CommentFormState { author: string; text: string;}class CommentForm extends React.Component { public state: CommentFormState; constructor(props) { super(props); this.state = { author: '', text: '' }; } handleAuthorChange = (e) => { this.setState({ author: e.target.value, text: this.state.text }); } handleTextChange = (e) => { this.setState({ author: this.state.author, text: e.target.value }); } render() { return ( ); }}
请特别注意事件处理程序跟官方教程的不同之处。
从程序中可以看出,为了使state和组件保持一致,使用了onChange。事件
React使用驼峰命名规则给组件绑定事件处理。我们将 onChange
绑定到两个 <input>
元素。现在,当用户输入文本时,绑定的处理函数触发回调,组件的 state
将会更改。随后,input元素的渲染值会更新反应当前组件的状态。
提交表单
现在让表单进行互动,当用户提交表单后,我们将清除它,提交一个请求到服务器,并刷新评论列表。开始,让我们侦听表单提交事件并清除它。
///export =CommentForm;interface CommentFormState { author: string; text: string;}class CommentForm extends React.Component { public state: CommentFormState; constructor(props) { super(props); this.state = { author: '', text: '' }; } handleAuthorChange = (e) => { this.setState({ author: e.target.value, text: this.state.text }); } handleTextChange = (e) => { this.setState({ author: this.state.author, text: e.target.value }); } handleSubmit=(e)=>{ e.preventDefault(); var author = this.state.author.trim(); var text = this.state.text.trim(); if (!text || !author) { return; } // TODO: send request to the server this.setState({ author: '', text: '' }); } render() { return ( ); }}
我们绑定 onSubmit 来处理表单,当表单输入有效后提交并请除清单字段。请用 preventDefault()
是为了阻止浏览器缺省的提交表单动作。
用回调函数作为props
当用户提交一个评论时,我们需要刷新评论列表包含新的这个评论。在CommentBox
中放置 所有的逻辑是合适的,因为CommentBox
包含了代表当前评论列表的状态。
handleCommentSubmit
)到子组件。绑定到子组件的 onCommentSubmit
事件。无论什么时候触发,回调函数都会被调用。 ////// import CommentList = require("./CommentList");import CommentForm = require("./CommentForm");export =CommentBox;interface CommentBoxProps { url: string; pollInterval: number;}interface CommentBoxState { data: any;}class CommentBox extends React.Component { public state: CommentBoxState; constructor(props: CommentBoxProps) { super(props); this.state = { data: [] }; } loadCommentsFromServer() { $.ajax({ url: this.props.url, dataType: 'json', cache: false, success: function (data) { this.setState({ data: data }); }.bind(this), error: function (xhr, status, err) { console.error(this.props.url, status, err.toString()); }.bind(this) }); } handleCommentSubmit = (comment) => { //Todo:提交到服务器并刷新列表 } componentDidMount() { //this.loadCommentsFromServer(); //setInterval(this.loadCommentsFromServer, this.props.pollInterval); //setInterval(() => this.loadCommentsFromServer, this.props.pollInterval); setInterval(() => this.loadCommentsFromServer(), this.props.pollInterval); } render() { return ( ); }}评论
当用户提交表单时,让我们从CommentForm
中调用回调函数。
///export =CommentForm;interface CommentFormState { author: string; text: string;}class CommentForm extends React.Component { public state: CommentFormState; constructor(props) { super(props); this.state = { author: '', text: '' }; } handleAuthorChange = (e) => { this.setState({ author: e.target.value, text: this.state.text }); } handleTextChange = (e) => { this.setState({ author: this.state.author, text: e.target.value }); } handleSubmit=(e)=>{ e.preventDefault(); var author = this.state.author.trim(); var text = this.state.text.trim(); if (!text || !author) { return; } this.props.onCommentSubmit({ author: author, text: text }); this.setState({ author: '', text: '' }); } render() { return ( ); }}
现在回调函数已经就绪,我们现在要做的只是提交到服务器并刷新列表:
优化:提前更新
我们的应用现在已经功能完备了,但是新添加的评论需要等待对服务器的请求完成后才会出现在列表中, 这样感觉会慢一点。我们可以提前将这条评论放到列表中让应用感觉更快。
////// import CommentList = require("./CommentList");import CommentForm = require("./CommentForm");export =CommentBox;interface CommentBoxProps { url: string; pollInterval: number;}interface CommentBoxState { data: any;}class CommentBox extends React.Component { public state: CommentBoxState; constructor(props: CommentBoxProps) { super(props); this.state = { data: [] }; } loadCommentsFromServer() { $.ajax({ url: this.props.url, dataType: 'json', cache: false, success: function (data) { this.setState({ data: data }); }.bind(this), error: function (xhr, status, err) { console.error(this.props.url, status, err.toString()); }.bind(this) }); } handleCommentSubmit = (comment) => { var comments = this.state.data; // Optimistically set an id on the new comment. It will be replaced by an // id generated by the server. In a production application you would likely // not use Date.now() for this and would have a more robust system in place. comment.id = Date.now(); var newComments = comments.concat([comment]); this.setState({ data: newComments }); $.ajax({ url: this.props.url, dataType: 'json', type: 'POST', data: comment, success: function (data) { this.setState({ data: data }); }.bind(this), error: function (xhr, status, err) { this.setState({ data: comments }); console.error(this.props.url, status, err.toString()); }.bind(this) }); } componentDidMount() { //this.loadCommentsFromServer(); //setInterval(this.loadCommentsFromServer, this.props.pollInterval); //setInterval(() => this.loadCommentsFromServer, this.props.pollInterval); setInterval(() => this.loadCommentsFromServer(), this.props.pollInterval); } render() { return ( ); }}评论
OK,教程到此结束,因为不是专业写作,肯定有很多不足之处,欢迎各种拍砖和讨论。等时间充裕时我再整理一下整个教程,有可能的话在GitHub上把所有源代码上传以便大家直接开箱使用。