当前位置: 时代头条 > 正文

React 快速入门

立即观看完整教程,请直接访问极客学院 Wiki

React 快速入门

入门

JSFiddle

开始 Hack React 的最简单的方法是用下面 JSFiddle 的 Hello Worlds:

  • React JSFiddle

  • React JSFiddle without JSX

入门教程包 (Starter Kit)

开始先下载入门教程包

在入门教程包的根目录,创建一个含有下面代码的 helloworld.html

在 JavaScript 代码里写着 XML 格式的代码称为 JSX;可以去 JSX 语法 里学习更多 JSX 相关的知识。为了把 JSX 转成标准的 JavaScript,我们用

离线转换

先安装命令行工具(依赖 npm):

npm install -g react-tools

然后把你的 src/helloworld.js 文件转成标准的 JavaScript:

jsx --watch src/ build/

只要你修改了, build/helloworld.js 文件会自动生成。

React.render(

React.createElement('h1', null, 'Hello, world!'),

document.getElementById('example')

);

对照下面更新你的 HTML 代码

Hello React!

想用 CommonJS?

如果你想在 browserify,webpack 或者或其它兼容CommonJS的模块系统里使用 React,只要使用 react npm 包 即可。而且,jsx 转换工具可以很轻松的地集成到大部分打包系统里(不仅仅是 CommonJS)。

下一步

去看看入门教程和入门教程包 examples 目录下的其它例子学习更多。

我们还有一个社区开发者共建的 Wiki:workflows, UI-components, routing, data management etc.

恭喜你,欢迎来到 React 的世界。

教程

我们将构建一个简单却真实的评论框,你可以将它放入你的博客,类似disqus、livefyre、facebook提供的实时评论的基础版。

我们将提供以下内容:

  • 一个展示所有评论的视图

  • 一个提交评论的表单

  • 用于构建自定制后台的接口链接(hooks)

同时也包含一些简洁的特性:

  • 评论体验优化: 评论在保存到服务器之前就展现在评论列表,因此用户体验很快。

  • 实时更新: 其他用户的评论将会实时展示。

  • Markdown格式: 用户可以使用MarkDown格式来编辑文本。

想要跳过所有的内容,只查看源代码?

所有代码都在GitHub。

运行一个服务器

虽然它不是入门教程的必需品,但接下来我们会添加一个功能,发送 POST ing请求到服务器。如果这是你熟知的事并且你想创建你自己的服务器,那么就这样干吧。而对于另外的一部分人,为了让你集中精力学习,而不用担忧服务器端方面,我们已经用了以下一系列的语言编写了简单的服务器代码 - JavaScript(使用 Node.js),Python和Ruby。所有代码都在GitHub。你可以查看代码或者下载 zip 文件来开始学习。

开始使用下载的教程,只需开始编辑 public/index.html

开始学习

在这个教程里面,我们将使用放在 CDN 上预构建好的 JavaScript 文件。打开你最喜欢的编辑器,创建一个新的 HTML 文档:

Hello React

在本教程其余的部分,我们将在此 script 标签中编写我们的 JavaScript 代码。

注意:

因为我们想简化 ajax 请求代码,所以在这里引入 jQuery,但是它对 React 并不是必须的。

你的第一个组件

React 中全是模块化、可组装的组件。以我们的评论框为例,我们将有如下的组件结构:

- CommentBox

- CommentList

- Comment

- CommentForm

让我们构造 CommentBox 组件,它只是一个简单的

而已:

// tutorial1.js

var CommentBox = React.createClass({

render: function() {

return (

Hello, world! I am a CommentBox.

);

}

});

React.render(

,

document.getElementById('content')

);

JSX语法

首先你注意到 JavaScript 代码中 XML 式的语法语句。我们有一个简单的预编译器,用于将这种语法糖转换成纯的 JavaScript 代码:

// tutorial1-raw.js

var CommentBox = React.createClass({displayName: 'CommentBox',

render: function() {

return (

React.createElement('div', {className: "commentBox"},

"Hello, world! I am a CommentBox."

)

);

}

});

React.render(

React.createElement(CommentBox, null),

document.getElementById('content')

);

JSX 语法是可选的,但是我们发现 JSX 语句比纯 JavaScript 更加容易使用。阅读更多关于 JSX 语法的文章。

发生了什么

我们通过 JavaScript 对象传递一些方法到 React.createClass() 来创建一个新的React组件。其中最重要的方法是 render,该方法返回一颗 React 组件树,这棵树最终将会渲染成 HTML。

这个

标签不是真实的DOM节点;他们是 React div 组件的实例。你可以认为这些就是React知道如何处理的标记或者一些数据。React 是安全的。我们不生成 HTML 字符串,因此默认阻止了 XSS 攻击。

你没有必要返回基本的 HTML。你可以返回一个你(或者其他人)创建的组件树。这就使得 React 变得组件化:一个关键的前端维护原则。

React.render() 实例化根组件,启动框架,注入标记到原始的 DOM 元素中,作为第二个参数提供。

制作组件

让我们为 CommentListCommentForm 构建骨架,这也会是一些简单的

// tutorial2.js

var CommentList = React.createClass({

render: function() {

return (

Hello, world! I am a CommentList.

);

}

});

var CommentForm = React.createClass({

render: function() {

return (

Hello, world! I am a CommentForm.

);

}

});

下一步,更新 CommentBox 组件,使用这些新的组件:

// tutorial3.js

var CommentBox = React.createClass({

render: function() {

return (

Comments

);

}

});

注意我们是如何混合 HTML 标签和我们创建的组件。HTML 组件就是普通的 React 组件,就像你定义的一样,只有一点不一样。JSX 编译器会自动重写 HTML 标签为 React.createElement(tagName) 表达式,其它什么都不做。这是为了避免全局命名空间污染。

组件属性

让我们创建我们的第三个组件,Comment。我们想传递给它作者名字和评论文本,以便于我们能够对每一个独立的评论重用相同的代码。首先让我们添加一些评论到 CommentList

// tutorial4.js

var CommentList = React.createClass({

render: function() {

return (

This is one comment

This is *another* comment

);

}

});

请注意,我们已经从父节点 CommentList 组件传递给子节点 Comment 组件一些数据。例如,我们传递了 Pete Hunt (通过一个属性)和 This is one comment * (通过类似于XML的子节点)给第一个 Comment。从父节点传递到子节点的数据称为 *props**,是属性(properties)的缩写。

使用props

让我们创建评论组件。通过 props,就能够从中读取到从 CommentList 传递过来的数据,然后渲染一些标记:

// tutorial5.js

var Comment = React.createClass({

render: function() {

return (

{this.props.author}

{this.props.children}

);

}

});

在 JSX 中通过将 JavaScript 表达式放在大括号中(作为属性或者子节点),你可以生成文本或者 React 组件到节点树中。我们访问传递给组件的命名属性作为 this.props 的键,任何内嵌的元素作为 this.props.children

添加 Markdown

Markdown 是一种简单的格式化内联文本的方式。例如,用星号包裹文本将会使其强调突出。

首先,添加第三方的 Showdown 库到你的应用。这是一个JavaScript库,处理 Markdown 文本并且转换为原始的 HTML。这需要在你的头部添加一个 script 标签(我们已经在 React 操练场上包含了这个标签):

Hello React

下一步,让我们转换评论文本为 Markdown 格式,然后输出它:

// tutorial6.js

var converter = new Showdown.converter();

var Comment = React.createClass({

render: function() {

return (

{this.props.author}

{converter.makeHtml(this.props.children.toString())}

);

}

});

我们在这里唯一需要做的就是调用 Showdown 库。我们需要把this.props.children从 React 的包裹文本转换成 Showdown 能处理的原始的字符串,所以我们显示地调用了toString()

但是这里有一个问题!我们渲染的评论在浏览器里面看起来像这样:“

This is another comment

”。我们想这些标签真正地渲染成 HTML。

那是 React 在保护你免受 XSS 攻击。这里有一种方法解决这个问题,但是框架会警告你别使用这种方法:

// tutorial7.js

var converter = new Showdown.converter();

var Comment = React.createClass({

render: function() {

var rawMarkup = converter.makeHtml(this.props.children.toString());

return (

{this.props.author}

);

}

});

这是一个特殊的 API,故意让插入原始的 HTML 变得困难,但是对于 Showdown,我们将利用这个后门。

记住: 使用这个功能,你会依赖于 Showdown 的安全性。

接入数据模型

到目前为止,我们已经在源代码里面直接插入了评论数据。相反,让我们渲染一小块JSON数据到评论列表。最终,数据将会来自服务器,但是现在,写在你的源代码中:

// tutorial8.js

var data = [

{author: "Pete Hunt", text: "This is one comment"},

{author: "Jordan Walke", text: "This is *another* comment"}

];

我们需要用一种模块化的方式将数据传入到 CommentList。修改 CommentBoxReact.render() 方法,通过 props 传递数据到CommentList

// tutorial9.js

var CommentBox = React.createClass({

render: function() {

return (

Comments

);

}

});

React.render(

,

document.getElementById('content')

);

现在数据在 CommentList 中可用了,让我们动态地渲染评论:

// tutorial10.js

var CommentList = React.createClass({

render: function() {

var commentNodes = this.props.data.map(function (comment) {

return (

{comment.text}

);

});

return (

{commentNodes}

);

}

});

就是这样!

从服务器获取数据

让我们用一些从服务器获取的动态数据替换硬编码的数据。我们将移除数据属性,用获取数据的URL来替换它:

// tutorial11.js

React.render(

,

document.getElementById('content')

);

这个组件和前面的组件是不一样的,因为它必须重新渲染自己。该组件将不会有任何数据,直到请求从服务器返回,此时该组件或许需要渲染一些新的评论。

响应状态变化(Reactive state)

到目前为止,每一个组件都根据自己的 props 渲染了自己一次。props 是不可变的:它们从父节点传递过来,被父节点“拥有”。为了实现交互,我们给组件引进了可变的 statethis.state 是组件私有的,可以通过调用 this.setState() 来改变它。当状态更新之后,组件重新渲染自己。

render() methods are written declaratively as functions of this.props and this.state. 框架确保UI始终和输入保持一致。

当服务器获取数据的时候,我们将会用已有的数据改变评论。让我们给 CommentBox 组件添加一个评论数组作为它的状态:

// tutorial12.js

var CommentBox = React.createClass({

getInitialState: function() {

return {data: []};

},

render: function() {

return (

Comments

);

}

});

getInitialState()在组件的生命周期中仅执行一次,设置组件的初始化状态。

更新状态

当组件第一次创建的时候,我们想从服务器获取(使用GET方法)一些JSON数据,更新状态,反映出最新的数据。在真实的应用中,这将会是一个动态功能点,但是对于这个例子,我们将会使用一个静态的JSON文件来使事情变得简单:

// tutorial13.json

[

{"author": "Pete Hunt", "text": "This is one comment"},

{"author": "Jordan Walke", "text": "This is *another* comment"}

]

我们将会使用jQuery帮助发出一个一步的请求到服务器。

注意:因为这会变成一个AJAX应用,你将会需要使用一个web服务器来开发你的应用,而不是一个放置在你的文件系统上面的一个文件。如上所述,我们已经在GitHub上面提供了几个你可以使用的服务器。这些服务器提供了你学习下面教程所需的功能。

// tutorial13.js

var CommentBox = React.createClass({

getInitialState: function() {

return {data: []};

},

componentDidMount: function() {

$.ajax({

url: this.props.url,

dataType: 'json',

success: function(data) {

this.setState({data: data});

}.bind(this),

error: function(xhr, status, err) {

console.error(this.props.url, status, err.toString());

}.bind(this)

});

},

render: function() {

return (

Comments

);

}

});

在这里,componentDidMount是一个在组件被渲染的时候React自动调用的方法。动态更新的关键点是调用this.setState()。我们把旧的评论数组替换成从服务器拿到的新的数组,然后UI自动更新。正是有了这种响应式,一个小的改变都会触发实时的更新。这里我们将使用简单的轮询,但是你可以简单地使用WebSockets或者其它技术。

// tutorial14.js

var CommentBox = React.createClass({

loadCommentsFromServer: function() {

$.ajax({

url: this.props.url,

dataType: 'json',

success: function(data) {

this.setState({data: data});

}.bind(this),

error: function(xhr, status, err) {

console.error(this.props.url, status, err.toString());

}.bind(this)

});

},

getInitialState: function() {

return {data: []};

},

componentDidMount: function() {

this.loadCommentsFromServer();

setInterval(this.loadCommentsFromServer, this.props.pollInterval);

},

render: function() {

return (

Comments

);

}

});

React.render(

,

document.getElementById('content')

);

我们在这里所做的就是把AJAX调用移到一个分离的方法中去,组件第一次加载以及之后每隔两秒钟,调用这个方法。尝试在你的浏览器中运行,然后改变comments.json文件;在两秒钟之内,改变将会显示出来!

添加新的评论

现在是时候构造表单了。我们的CommentForm组件应该询问用户的名字和评论内容,然后发送一个请求到服务器,保存这条评论。

// tutorial15.js

var CommentForm = React.createClass({

render: function() {

return (

);

}

});

让我们使表单可交互。当用户提交表单的时候,我们应该清空表单,提交一个请求到服务器,然后刷新评论列表。首先,让我们监听表单的提交事件和清空表单。

// tutorial16.js

var CommentForm = React.createClass({

handleSubmit: function(e) {

e.preventDefault();

var author = this.refs.author.getDOMNode().value.trim();

var text = this.refs.text.getDOMNode().value.trim();

if (!text || !author) {

return;

}

// TODO: send request to the server

this.refs.author.getDOMNode().value = '';

this.refs.text.getDOMNode().value = '';

return;

},

render: function() {

return (

);

}

});

事件

React使用驼峰命名规范的方式给组件绑定事件处理器。我们给表单绑定一个onSubmit处理器,用于当表单提交了合法的输入后清空表单字段。

在事件回调中调用preventDefault()来避免浏览器默认地提交表单。

Refs

我们利用Ref属性给子组件命名,this.refs引用组件。我们可以在组件上调用getDOMNode()获取浏览器本地的DOM元素。

回调函数作为属性

当用户提交评论的时候,我们需要刷新评论列表来加进这条新评论。在CommentBox中完成所有逻辑是合适的,因为CommentBox拥有代表评论列表的状态(state)。

我们需要从子组件传回数据到它的父组件。我们在父组件的render方法中做这件事:传递一个新的回调函数(handleCommentSubmit)到子组件,绑定它到子组件的onCommentSubmit事件上。无论事件什么时候触发,回调函数都将会被调用:

// tutorial17.js

var CommentBox = React.createClass({

loadCommentsFromServer: function() {

$.ajax({

url: this.props.url,

dataType: 'json',

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: function(comment) {

// TODO: submit to the server and refresh the list

},

getInitialState: function() {

return {data: []};

},

componentDidMount: function() {

this.loadCommentsFromServer();

setInterval(this.loadCommentsFromServer, this.props.pollInterval);

},

render: function() {

return (

Comments

);

}

});

当用户提交表单的时候,让我们在CommentForm中调用这个回调函数:

// tutorial18.js

var CommentForm = React.createClass({

handleSubmit: function(e) {

e.preventDefault();

var author = this.refs.author.getDOMNode().value.trim();

var text = this.refs.text.getDOMNode().value.trim();

if (!text || !author) {

return;

}

this.props.onCommentSubmit({author: author, text: text});

this.refs.author.getDOMNode().value = '';

this.refs.text.getDOMNode().value = '';

return;

},

render: function() {

return (

);

}

});

现在回调函数已经就绪,唯一我们需要做的就是提交到服务器,然后刷新列表:

// tutorial19.js

var CommentBox = React.createClass({

loadCommentsFromServer: function() {

$.ajax({

url: this.props.url,

dataType: 'json',

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: function(comment) {

$.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) {

console.error(this.props.url, status, err.toString());

}.bind(this)

});

},

getInitialState: function() {

return {data: []};

},

componentDidMount: function() {

this.loadCommentsFromServer();

setInterval(this.loadCommentsFromServer, this.props.pollInterval);

},

render: function() {

return (

Comments

);

}

});

优化:提前更新

我们的应用现在已经完成了所有功能,但是在你的评论出现在列表之前,你必须等待请求完成,感觉很慢。我们可以提前添加这条评论到列表中,从而使应用感觉更快。

// tutorial20.js

var CommentBox = React.createClass({

loadCommentsFromServer: function() {

$.ajax({

url: this.props.url,

dataType: 'json',

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: function(comment) {

var comments = this.state.data;

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) {

console.error(this.props.url, status, err.toString());

}.bind(this)

});

},

getInitialState: function() {

return {data: []};

},

componentDidMount: function() {

this.loadCommentsFromServer();

setInterval(this.loadCommentsFromServer, this.props.pollInterval);

},

render: function() {

return (

Comments

);

}

});

祝贺你!

你刚刚通过一些简单步骤够早了一个评论框。了解更多关于为什么使用React的内容,或者深入学习API参考,开始专研!祝你好运!

深入理解 React

这是一篇源自官方博客 的文章。

在我看来, React 是较早使用 JavaScript 构建大型、快速的 Web 应用程序的技术方案。它已经被我们广泛应用于 Facebook 和 Instagram 。

React 众多优秀特征中的其中一部分就是,教会你去重新思考如何构建应用程序。

本文中,我将跟你一起使用 React 构建一个具备搜索功能的产品列表。

注意:

如果你无法看到本页内嵌的代码片段,请确认你不是用 https 协议加载本页的。

从原型( mock )开始

假设我们已经拥有了一个 JSON API 和设计师设计的原型。我们的设计师显然不够好,因为原型看起来如下:

React 快速入门

JSON接口返回数据如下:

[

{category: "Sporting Goods", price: "$49.99", stocked: true, name: "Football"},

{category: "Sporting Goods", price: "$9.99", stocked: true, name: "Baseball"},

{category: "Sporting Goods", price: "$29.99", stocked: false, name: "Basketball"},

{category: "Electronics", price: "$99.99", stocked: true, name: "iPod Touch"},

{category: "Electronics", price: "$399.99", stocked: false, name: "iPhone 5"},

{category: "Electronics", price: "$199.99", stocked: true, name: "Nexus 7"}

];

第一步:拆分用户界面为一个组件树

你要做的第一件事是,为所有组件(及子组件)命名并画上线框图。假如你和设计师一起工作,也许他们已经完成了这项工作,所以赶紧去跟他们沟通!他们的 Photoshop 图层名也许最终可以直接用于你的 React 组件名。

然而你如何知道哪些才能成为组件?想象一下,当你创建一些函数或对象时,用到一些类似的技术。其中一项技术就是单一功能原则,指的是,理想状态下一个组件应该只做一件事,假如它功能逐渐变大就需要被拆分成更小的子组件。

由于你经常需要将一个JSON数据模型展示给用户,因此你需要检查这个模型结构是否正确以便你的 UI (在这里指组件结构)是否能够正确的映射到这个模型上。这是因为用户界面和数据模型在 信息构造 方面都要一致,这意味着将你可以省下很多将 UI 分割成组件的麻烦事。你需要做的仅仅只是将数据模型分隔成一小块一小块的组件,以便它们都能够表示成组件。

React 快速入门

由此可见,我们的 app 中包含五个组件。下面我已经用斜体标示出每个组件对应的数据。

  1. FilterableProductTable (橘色): 包含整个例子的容器

  2. SearchBar (蓝色): 接受所有 用户输入( user input )

  3. ProductTable (绿色): 根据 用户输入( user input ) 过滤和展示 数据集合( data collection )

  4. ProductCategoryRow (青色): 为每个 分类( category ) 展示一列表头

  5. ProductRow (红色): 为每个 产品( product ) 展示一列

如果你仔细观察 ProductTable ,你会发现表头(包含“ Name ”和“ Price ”标签)并不是单独的组件。这只是一种个人偏好,也有一定的争论。在这个例子当中,我把表头当做 ProductTable 的一部分,因为它是渲染“数据集合”的一份子,这也是 ProductTable 的职责。但是,当这个表头变得复杂起来的时候(例如,添加排序功能),就应该单独地写一个 ProductTableHeader 组件。

既然我们在原型当中定义了这个组件,让我们把这些元素组成一棵树形结构。这很简单。被包含在其它组件中的组件在属性机构中应该是子级:

  • FilterableProductTable

    • ProductCategoryRow

    • ProductRow

    • SearchBar -ProductTable

第二步: 利用 React ,创建应用的一个静态版本

既然已经拥有了组件树,是时候开始实现应用了。最简单的方式就是创建一个应用,这个应用将数据模型渲染到 UI 上,但是没有交互功能。拆分这两个过程是最简单的,因为构建一个静态的版本仅需要大量的输入,而不需要思考;但是添加交互功能却需要大量的思考和少量的输入。我们将会知道这是为什么。

为了创建一个渲染数据模型的应用的静态版本,你将会构造一些组件,这些组件重用其它组件,并且通过 props 传递数据。 props 是一种从父级向子级传递数据的方式。如果你对 state 概念熟悉,那么*不要使用 state *来构建这个静态版本。 state 仅用于实现交互功能,也就是说,数据随着时间变化。因为这是一个静态的应用版本,所以你并不需要 state 。

你可以从上至下或者从下至上来构建应用。也就是说,你可以从属性结构的顶部开始构建这些组件(例如,从FilterableProductTable 开始),或者从底部开始( ProductRow )。在简单的应用中,通常情况下从上至下的方式更加简单;在大型的项目中,从下至上的方式更加简单,这样也可以在构建的同时写测试代码。

在这步结束的时候,将会有一个可重用的组件库来渲染数据模型。这些组件将会仅有 render() 方法,因为这是应用的一个静态版本。位于树形结构顶部的组件( FilterableProductTable )将会使用数据模型作为 prop 。如果你改变底层数据模型,然后再次调用React.render() , UI 将会更新。查看 UI 如何被更新和什么地方改变都是很容易的,因为 React 的单向数据流(也被称作“单向绑定”)保持了一切东西模块化,很容易查错,并且速度很快,没有什么复杂的。

如果你在这步中需要帮助,请查看 React 文档。

穿插一小段内容: props 与 state 比较

在 React 中有两种类型的数据“模型”: props 和 state 。理解两者的区别是很重要的;如果你不太确定两者有什么区别,请大致浏览一下官方的 React 文档。

第三步:识别出最小的(但是完整的)代表 UI 的 state

为了使 UI 可交互,需要能够触发底层数据模型的变化。 React 通过 state 使这变得简单。

为了正确构建应用,首先需要考虑应用需要的最小的可变 state 数据模型集合。此处关键点在于精简:不要存储重复的数据。构造出绝对最小的满足应用需要的最小 state 是有必要的,并且计算出其它强烈需要的东西。例如,如果构建一个 TODO 列表,仅保存一个 TODO 列表项的数组,而不要保存另外一个指代数组长度的 state 变量。当想要渲染 TODO 列表项总数的时候,简单地取出 TODO 列表项数组的长度就可以了。

思考示例应用中的所有数据片段,有:

  • 最初的 products 列表

  • 用户输入的搜索文本

  • 复选框的值

  • 过滤后的 products 列表

让我们分析每一项,指出哪一个是 state 。简单地对每一项数据提出三个问题:

  1. 是否是从父级通过 props 传入的?如果是,可能不是 state 。

  2. 是否会随着时间改变?如果不是,可能不是 state 。

  3. 能根据组件中其它 state 数据或者 props 计算出来吗?如果是,就不是 state 。

初始的 products 列表通过 props 传入,所以不是 state 。搜索文本和复选框看起来像是 state ,因为它们随着时间改变,也不能根据其它数据计算出来。最后,过滤的 products 列表不是 state ,因为可以通过搜索文本和复选框的值从初始的 products 列表计算出来。

所以最终, state 是:

  • 用户输入的搜索文本

  • 复选框的值

第四步:确认 state 的生命周期

OK,我们辨别出了应用的 state 数据模型的最小集合。接下来,需要指出哪个组件会改变或者说拥有这个 state 数据模型。

记住: React 中数据是沿着组件树从上到下单向流动的。可能不会立刻明白哪个组件应该拥有哪些 state 数据模型。这对新手通常是最难理解和最具挑战的,因此跟随以下步骤来弄清楚这点:

对于应用中的每一个 state 数据:

  • 找出每一个基于那个 state 渲染界面的组件。

  • 找出共同的祖先组件(某个单个的组件,在组件树中位于需要这个 state 的所有组件的上面)。

  • 要么是共同的祖先组件,要么是另外一个在组件树中位于更高层级的组件应该拥有这个 state 。

  • 如果找不出拥有这个 state 数据模型的合适的组件,创建一个新的组件来维护这个 state ,然后添加到组件树中,层级位于所有共同拥有者组件的上面。

让我们在应用中应用这个策略:

  • ProductTable 需要基于 state 过滤产品列表,SearchBar 需要显示搜索文本和复选框状态。

  • 共同拥有者组件是 FilterableProductTable

  • 理论上,过滤文本和复选框值位于 FilterableProductTable 中是合适的。

太酷了,我们决定了 state 数据模型位于 FilterableProductTable 之中。首先,给 FilterableProductTable 添加getInitialState() 方法,该方法返回 {filterText: '', inStockOnly: false} 来反映应用的初始化状态。然后传递filterTextinStockOnlyProductTableSearchBar 作为 prop 。最后,使用这些 props 来过滤 ProductTable 中的行,设置在 SearchBar 中表单字段的值。

你可以开始观察应用将会如何运行:设置 filterText"ball" ,然后刷新应用。将会看到数据表格被正确更新了。

第五步:添加反向数据流

到目前为止,已经构建了渲染正确的基于 props 和 state 的沿着组件树从上至下单向数据流动的应用。现在,是时候支持另外一种数据流动方式了:组件树中层级很深的表单组件需要更新 FilterableProductTable 中的 state 。

React 让这种数据流动非常明确,从而很容易理解应用是如何工作的,但是相对于传统的双向数据绑定,确实需要输入更多的东西。 React 提供了一个叫做 ReactLink 的插件来使其和双向数据绑定一样方便,但是考虑到这篇文章的目的,我们将会保持所有东西都直截了当。

如果你尝试在示例的当前版本中输入或者选中复选框,将会发现 React 会忽略你的输入。这是有意的,因为已经设置了 inputvalue 属性,使其总是与从 FilterableProductTable 传递过来的 state 一致。

让我们思考下我们希望发生什么。我们想确保无论何时用户改变了表单,都要更新 state 来反映用户的输入。由于组件只能更新自己的 state , FilterableProductTable 将会传递一个回调函数给 SearchBar ,此函数将会在 state 应该被改变的时候触发。我们可以使用 input 的 onChange 事件来监听用户输入,从而确定何时触发回调函数。 FilterableProductTable 传递的回调函数将会调用setState() ,然后应用将会被更新。

虽然这听起来有很多内容,但是实际上仅仅需要几行代码。并且关于数据在应用中如何流动真的非常清晰明确。

就这么简单

希望以上内容让你明白了如何思考用 React 去构造组件和应用。虽然可能比你之前要输入更多的代码,记住,读代码的时间远比写代码的时间多,并且阅读这种模块化的清晰的代码是相当容易的。当你开始构建大型的组件库的时候,你将会非常感激这种清晰性和模块化,并且随着代码的复用,整个项目代码量就开始变少了 :)。

最新文章

本站部分内容搜集自互联网,如对您造成侵权,请您联系站长。

学生时代 联系e-mail:kefu#stutimes.com