本文案例、代码及主要内容基于知乎前端专栏翻译的文章《深入浅出React Native:使用JavaScript构建原生应用》《Introducing React Native: Building Apps with JavaScript》而写。
在原文的基础上做了一些校对,并增加了android得环境安装和调试的内容。
由我书写的注释将会显示左边这个紫色竖线(#523f6d),而原文的注释为蓝色的(#559bce)。
内容传送门
一、《深入浅出R跟我学用React Native构建原生应用(一)
二、《深入浅出R跟我学用React Native构建原生应用(二)
三、《深入浅出R跟我学用React Native构建原生应用(三)
四、《深入浅出R跟我学用React Native构建原生应用(四)
五、《深入浅出R跟我学用React Native构建原生应用(五)
六、《深入浅出R跟我学用React Native构建原生应用(六)
添加组件状态
每个 React 组件都带有一个key-value存储的状态对象,你必须在组件渲染之前设置其初始状态。
在 SearchPage.js 中,我们对 SearchPage 类中,render方法前添加以下的代码。
constructor(props) {
super(props);
this.state = {
searchString: 'london'
};
}
现在你的组件拥有一个状态变量:searchString ,且初始值被设置为 london 。
这时候你需要利用起组件中的状态了。在render方法中,用以下的代码替换TextInput元素中的内容:
<TextInput
style={styles.searchInput}
value={this.state.searchString}
placeholder='Search via name or postcode'/>
这一步设置了 TextInput 组件 value 属性的值,这个值用于把状态变量 searchString 的当前值作为展示给用户的文字。我们已经考虑初始值的设定了,但如果用户编辑这里的文字会发生什么呢?
第一步需要建立一个方法来处理事件。在 SearchPage 类中添加以下的代码:
onSearchTextChanged(event) {
console.log('onSearchTextChanged');
this.setState({ searchString: event.nativeEvent.text });
console.log(this.state.searchString);
}
上面的代码从 events 中取出了 text 属性的值,并用于更新组件的状态。这里面也添加了一些有用的调试代码。
当文字改变时,需要让这个方法被调用,调用后的文字会通过 render 函数返回到组件中。因此我们需要在标签上添加一个 onChange 属性,添加后的标签如下所示:
<TextInput
style={styles.searchInput}
value={this.state.searchString}
onChange={this.onSearchTextChanged.bind(this)}
placeholder='Search via name or postcode'/>
当用户更改文本时,会调用 onChange 上 的函数;在本例中,则是 onSearchTextChanged 。
注意:你估计会对 bind(this) 语句有疑问。在 JavaScript 中,this 这个关键字有点不同于大多数其他语言;在 Swift 表示 “自身”。在这种情况中,bind 可以确保在 onSearchTextChanged 方法中, this 可以作为组件实例的引用。有关更多信息,请参见MDN this页面。
在你再次刷新你的应用程序之前,还有一个步骤:在 return 前添加以下语句,打印一条日志来记录 render() 函数的调用:
console.log('SearchPage.render');
你会从这些日志语句中学到一些很有趣的东西!:]
回到你的模拟器,然后按Cmd + R。您现在应该看到文本输入的初始值为 “london” ,编辑一下文本,从而在 Xcode 控制台中产生一些日志:
注意看上面的截图,日志打印的顺序看起来有些奇怪:
第一次调用 render() 函数用于设置视图。当文本变化时, onSearchTextChanged 函数被调用。之后,通过更新组件的状态来反映输入了新的文本,这会触发另一次 render 。 onSearchTextChanged() 函数也会被调用,会将改变的字符串打印出来。每当应用程序更新任何 React 组件,将会触发整个UI层的重新绘制,这会调用你所有组件的 render 方法。这是一个好主意,因为这样做把组件的渲染逻辑,从状态变化影响UI这一过程中完全解耦出来。
与其他大多数 UI 框架所不同的是,你既不需要在状态改变的时候去手动更新 UI ,或使用某种类型的绑定框架,来创建某种应用程序状态和它的 UI 表现的关联;例如,我的文章中讲的,通过ReactiveCocoa实现MVVM模式。
在 React 中,你不再需要担心 UI 的哪些部分可能受到状态变化的影响;你的整个应用程序的 UI,都可以简单地表示为一个函数的状态。
此时,你可能已经发现了这一概念中一个根本性的缺陷。是的,非常准确——性能!
你肯定不能在 UI 变化时,完全抛弃掉整个 UI 然后重新绘制吧
?这就是 React 高明的地方了。每当 UI 渲染出来后,render 方法会返回一颗视图渲染树,并与当前的 UIKit 视图进行比较。这个称之为 reconciliation 的过程的输出是一个简单的更新列表, React 会将这个列表应用到当前视图。这意味着,只有实际改变了的部分才会重新绘制。
这个令人拍案叫绝的崭新概念让ReactJS变得独特——virtual-DOM(文档对象模型,一个web文档的视图树)和 reconciliation 这些概念——被应用于iOS应用程序。
稍后你可以整理下思路,之后,在刚才的应用中你仍然有一些工作要做。日志代码增加了代码的繁琐性,已经不需要了,所以删除掉日志代码。
初始化搜索功能
为了实现搜索功能,你需要处理 “Go” 按钮的点击事件,调用对应的 API,并提供一个视觉效果,告诉用户正在做查询。
在 SearchPage.js 中,在构造函数中把初始状态更新成:
this.state = {
searchString: 'london',
isLoading: false
};
新的 isLoading 属性将会记录是否有请求正在处理的状态。
在 render 开始的添加如下逻辑:
var spinner = this.state.isLoading ?
( <ActivityIndicatorIOS
hidden='true'
size='large'/> ) :
( <View/>);
这是一个三元操作符,与 if 语句类似,即根据组件 isLoading 的状态,要么添加一个 indicator,要么添加一个空的 view。因为整个组件会不停地更新,所以你自由地混合 JSX 和 JavaSript 代码。
回到用 JSX 定义搜索界面的地方,在图片的下面添加:
{spinner}
给渲染“Go”的 TouchableHighlight 标记添加如下的属性:
onPress={this.onSearchPressed.bind(this)}
接下来,添加下面这两个方法到 SearchPage 类中:
_executeQuery(query) {
console.log(query);
this.setState({ isLoading: true });
}
onSearchPressed() {
var query = urlForQueryAndPage('place_name', this.state.searchString, 1);
this._executeQuery(query);
}
_executeQuery() 之后会进行真实的查询,现在的话就是简单输出一条信息到控制台,并且把 isLoading 设置为对应的值,这样 UI 就可以显示新的状态了。
提示:JavaScript 的类并没有访问修饰符,因此没有 “私有” 的该奶奶。因此常常会发现开发者使用一个下划线作为方法的前缀,来说明这些方法是私有方法。
当 “Go” 按钮被点击时,onSearchPressed() 将会被调用,开始查询。
最后,添加下面这个工具函数在定义 SearchPage 类的上面:
function urlForQueryAndPage(key, value, pageNumber) {
var data = {
country: 'uk',
pretty: '1',
encoding: 'json',
listing_type: 'buy',
action: 'search_listings',
page: pageNumber
};
data[key] = value;
var querystring = Object.keys(data)
.map(key => key + '=' + encodeURIComponent(data[key]))
.join('&');
return 'http://api.nestoria.co.uk/api?' + querystring;
};
- 这个函数并不依赖 SearchPage,因此被定义成了一个独立的函数,而不是类方法。他首先通过 data 来定义查询字符串所需要的参数,接着将 data 转换成需要的字符串格式,name=value 对,使用 & 符号分割。语法 => 是一个箭头函数,又一个对 JavaScript 语言的扩展,提供了这个便捷的语法来创建一个匿名函数。
回到模拟器,Cmd+R,重新加载应用,点击 “Go” 按钮。你可以看到 activity indicator 显示出来,再看看 Xcode 的控制台:
activity indicator 渲染了,并且作为请求的 URL 出现在输出中。把 URL 拷贝到浏览器中访问看看得到的结果。你会看到大量的 JSON 对象。别担心——你不需要理解它们,之后会使用代码来解析之。
提示:应用使用了 Nestoria 的 API 来做房产的搜索。API 返回的 JSON 数据非常的直白。但是你也可以看看文档了解更多细节,请求什么 URL 地址,以及返回数据的格式。
下一步就是从应用中发出请求。