props和state
- 我们使用两种数据来控制一个组件:props和state。
- props是在父组件中指定,而且一经指定,在被指定的组件的生命周期中则不再改变。对于需要改变的数据,我们需要使用state。
Props(属性)
- 大多数组件在创建时就可以使用各种参数来进行定制,用于定制的这些参数就称为props(属性)。
- 以常见的基础组件Image为例:在创建一个图片时,可以传入一个名为source的 prop 来指定要显示的图片的地址,以及使用名为style的 prop 来控制其样式。
例子(自定义组件)
自定义的组件也可以使用props:
通过在不同的场景使用不同的属性定制,可以尽量提高自定义组件的复用范畴。只需在render函数中引用this.props
,然后按需处理即可。
【可以联想一下react中的父子组件传值方式】
下面例子中,我们在Greeting组件中将name作为一个属性来定制,这样可以复用这一组件来制作各种不同的“问候语”:
【从react的角度理解, 父组件LotsOfGreetings1 在调用 子组件Greeting 时将name作为属性传值给子组件,在子组件中通过this.props.name
获取到父组件传递过来的属性值,最终显示在父组件中】
State(状态)
- props是在父组件中指定,而且一经指定,在被指定的组件的生命周期中则不再改变。对于需要改变的数据,我们需要使用state。
- 一般来说,你需要在class的 constructor(构造函数) 中初始化中一个state对象,然后在需要修改时调用setState方法。
- 在React中,状态(state)一旦发生变化,就会触发界面的重新渲染。一切界面变化都是状态state变化。
- state的修改必须通过setState()方法,this.state.num = 100; 这样的直接赋值修改无效!
- setState() 是异步操作,修改不会马上生效。
- State 的工作原理和 React.js 完全一致,所以对于处理 state 的一些更深入的细节,你可以参考react的笔记。
例子
假如我们需要制作一段不停闪烁的文字。
文字内容本身在组件创建时就已经指定好了,所以文字内容应该是一个prop。
而文字的显示或隐藏的状态(快速的显隐切换就产生了闪烁的效果)则是随着时间变化的,因此这一状态应该写到state中:
1 | import React, { Component } from 'react'; |
ref 获取某个子组件
- React 支持一种非常特殊的属性 Ref ,你可以用来绑定到 render() 输出的任何组件上。
- 例子
关于ref的理解:
ref : 指向某个子组件
this : 指向当前export的组件本身
this.refs : 指向当前export的组件的所有子组件
this.refs.ref_phoneInput : 指向当前import的自定义封装组件
this.refs.ref_phoneInput.refs : 指向当前import的自定义封装组件的所有子组件
this.refs.ref_phoneInput.refs.PhoneNumberInput : 指向当前import的自定义封装组件的其中一个子组件
例子:
注意:这里的this.refs.myInput就是组件实例,可以获取到实例的属性和方法。
基础组件
ScrollView 滚动
- ScrollView 是一个通用的可滚动的容器,你可以在其中放入多个组件和视图,而且这些组件并不需要是同类型的。
- ScrollView 不仅可以垂直滚动,还能水平滚动(通过horizontal属性来设置。)
- 属性值:当horizontal属性为true的时候,所有的子视图会在水平方向上排成一行,而不是默认的在垂直方向上排成一列。默认值为false。(更多属性可查看文档)
- ScrollView 适合用来显示数量不多的滚动元素。放置在ScrollView 中的所有组件都会被渲染,哪怕有些组件因为内容太长被挤出了屏幕外。如果你需要显示较长的滚动列表,那么应该使用功能差不多但性能更好的FlatList组件。
列表视图组件
和一般化用途的ScrollView不同,下面的列表组件只会渲染当前屏幕可见的元素,这样有利于显示大量的数据。
FlatList 长列表组件
- React Native 提供了几个适用于展示长列表数据的组件,一般而言我们会选用FlatList或是SectionList。(如果要渲染的是一组需要分组的数据,也许还带有分组标签的,那么SectionList将是个不错的选择)
- FlatList组件用于显示一个垂直的滚动列表,其中的元素之间结构近似而仅数据不同。
- 区别于ScrollView: FlatList更适于长列表数据,且元素个数可以增删。和ScrollView不同的是,FlatList并不立即渲染所有元素,而是优先渲染屏幕上可见的元素。
- 必须的两个属性:data(数组)和renderItem(事件)。data是列表的数据源,而renderItem则从数据源中逐个解析数据,然后返回一个设定好格式的组件来渲染。【事件:是指发生在该组件上的事情。】
属性 | 值 |
---|---|
data | 为了简化起见,data 属性目前只支持普通数组。如果需要使用其他特殊数据结构,例如 immutable 数组,请直接使用更底层的VirtualizedList组件 |
renderItem(事件) | 函数,可接受3个参数。renderItem({item, index, separators}); item:data中的各个数据项 index:与数据数组中该项对应的索引 separators:参考文档 作用:从数据源中逐个解析数据,然后返回一个设定好格式的组件来渲染各个数据 |
例子:
下面的例子创建了一个简单的FlatList,并预设了一些模拟数据data。
首先是初始化FlatList所需的data,其中的每一项(行)数据之后都在renderItem中被渲染成了Text组件,最后构成整个FlatList:
SectionList 【分组】长列表组件
- 如果要渲染的是一组需要分组的数据,也许还带有分组标签的,那么SectionList将是个不错的选择。
- 列表的一个常用场景就是从服务器端取回列表数据然后显示,要实现这一过程还需要学习React Native 的网络相关用法。
- 必须的属性:sections(数组)、renderItem(事件)。(更多属性可参考文档)
属性 | 值 |
---|---|
sections | 数组,用来渲染的数据,类似于FlatList中的data属性 |
renderItem | 函数,用来渲染每一个section中的每一个列表项的默认渲染器。必须返回一个react组件。可以在section级别上进行覆盖重写。 |
renderSectionHeader | 函数,在每个section的头部渲染。在iOS上,这些headers是默认粘接在ScrollView的顶部的 |
renderSectionFooter | 每个组的尾部组件 |
注意
- 当某行滑出渲染区域之外后,其内部状态将不会保留。请确保你在行组件以外的地方保留了数据。
- 本组件继承自
PureComponent
而非通常的Component
,这意味着如果其props在浅比较中是相等的,则不会重新渲染。所以请先检查你的 renderItem函数 所依赖的 props数据(包括 data属性 以及可能用到的 父组件的state )。如果是一个引用类型(Object或者数组都是引用类型),则需要先修改其引用地址(比如先复制到一个新的Object或者数组中【深拷贝】),然后再修改其值,否则界面很可能不会刷新。(这一段不了解的朋友建议先学习下js中的基本类型和引用类型。) - 为了优化内存占用同时保持滑动的流畅,列表内容会在屏幕外异步绘制。这意味着如果用户滑动的速度超过渲染的速度,则会先看到空白的内容。这是为了优化不得不作出的妥协,而我们也在设法持续改进。
- 默认情况下每行都需要提供一个不重复的key属性。你也可以提供一个keyExtractor函数来生成key。
其他组件
WebView 加载网页内容
WebView组件可以实现直接加载网页内容在React Native框架中显示。
更多组件:
更多组件可查询文档
网络
很多移动应用都需要从远程地址中获取数据或资源。你可能需要给某个 REST API 发起 POST 请求以提交用户数据,又或者可能仅仅需要从某个服务器上获取一些静态内容。
fetch的基本用法
- React Native 提供了和 web 标准一致的Fetch API,用于满足开发者访问网络的需求。
- 这篇文章只会列出 Fetch 的基本用法,并不会讲述太多细节,你可以使用你喜欢的搜索引擎去搜索fetch api关键字以了解更多信息。
- 可以参考视频教程
发起请求
要从任意地址获取内容的话,只需简单地将网址作为参数传递给 fetch 方法即可(fetch 这个词本身也就是获取的意思):
1 | fetch('https://mywebsite.com/mydata.json');//获取该网址中mydata.json的内容 |
fetch() 还有可选的第二个参数,可以用来定制 HTTP 请求一些参数。你可以指定 header 参数,或是指定使用 POST 方法,又或是提交数据等等:
1 | fetch('https://mywebsite.com/endpoint/', { |
提交数据的格式关键取决于 headers 中的Content-Type。Content-Type有很多种,对应 body 的格式也有区别。
到底应该采用什么样的Content-Type取决于服务器端,所以请和服务器端的开发人员沟通确定清楚。
常用的’Content-Type’除了上面的’application/json’,还有传统的网页表单形式,示例如下:
1 | fetch('https://mywebsite.com/endpoint/', { |
可以参考Fetch 请求文档来查看所有可用的参数。
注意:使用 Chrome 调试目前无法观测到 React Native 中的网络请求,可以使用第三方的react-native-debugger来进行观测。
补充:json()
- json() 方法接收一个 Response对象,读取 Response 对象并且将它设置为已读(因为 Responses 对象被设置为了 stream 的方式,所以它们只能被读取一次),并返回一个被解析为 JSON 格式的 Promise 对象。
- MDN文档
处理服务器的响应数据
上面的例子演示了如何发起请求。很多情况下,你还需要处理服务器回复的数据。
网络请求天然是一种异步操作(异步的意思是你应该趁这个时间去做点别的事情,比如显示 loading,而不是让界面卡住傻等)。无论请求成功与否,fetch()都返回一个 promise 对象,请求成功时 resolve 对应请求的 Response对象(如果数据获取失败就会到进入catch),这种模式可以简化异步风格的代码(别忘了 catch 住fetch可能抛出的异常,否则出错时你可能看不到任何提示):
1 | function getMoviesFromApiAsync() { |
fetch()返回的是promise 对象,请求成功时将获取到的数据(Response对象)传入并执行then(),response是Response对象,包含Header、status、statusText等属性。
Response对象 要获得具体数据需要使用.json()
(解析为JSON格式)、.text()
(解析为 USVString 格式)、.formData()
(用于FormData对象)等方法。response.json()
返回的是一个Promise对象,所以只能先return为下一个then()中的参数responseJson,再在下一层处理。【Promise 对象用于表示一个异步操作的最终完成 (或失败), 及其结果值。所以我们不能直接操作Promise对象,需要等异步操作成功以后使用then()取到成功后返回的数据,在then()中对数据进行操作】
注意
- 默认情况下,iOS 会阻止所有 http 的请求,以督促开发者使用 https。如果你仍然需要使用 http 协议,那么首先需要添加一个 App Transport Security 的例外,详细可参考这篇帖子。
- 从 Android9 开始,也会默认阻止 http 请求,请参考相关配置。
使用其他的网络库
- React Native 中已经内置了XMLHttpRequest API(也就是俗称的 ajax)。
- 一些基于 XMLHttpRequest 封装的第三方库也可以使用,例如frisbee或axios等。但注意不能使用 jQuery,因为 jQuery 中还使用了很多浏览器中才有而 RN 中没有的东西(所以也不是所有 web 中的 ajax 库都可以直接使用)。【想要使用其他的网络库可以参考这个文档】
- 但是**不推荐使用第三方库,建议React Native中使用fetch()**。
- 需要注意的是,安全机制与网页环境有所不同:在应用中你可以访问任何网站,没有跨域的限制。
fetch和axios相比
- 使用fetch()多了一步,axios通过get()获取到的直接是数据,而fetch()获取到的是Promise对象,还要通过
.json()
解析为JSON格式才能获取到数据。 - axios可参考笔记