加载中

As a JavaScript developer, I would've never thought that I could make native  mobile applications easily in JavaScript. Sure, we have tools such as  PhoneGap, but wrapping our browser based application in native  application leaves a lot to be desired.

It's all changed now - the React team over at Facebook have released  React Native. This not only allows us to use the React framework to  create applications that utilise native mobile components, but it does  that all in real time - meaning that we don't have to recompile our  applications as we develop on them - this makes it incredibly easy to  create mobile apps! Luckily I got to preview React Native in its beta  and even since then it's progressed massively.

Please note that currently iOS is supported. You will therefore need  to be running OS X with XCode in order to follow this tutorial.

作为一个 JavaScript 开发者,我之前从来没想过用 JavaScript 很容易地写原生移动应用。当然,我们已经有了如 PhoneGap 等工具,但在原生应用中封装一个基于浏览器的应用还有许多需要改进的地方。

现在这一切都改变了—— Facebook 的 React 团队发布了 React Native。它不仅可以让我们使用 React 框架来使用原生移动组件创建应用程序,但它使一切成为了现实——这意味着我们在开发应用时不需要重新编译——这使得它非常容易地创建移动应用!我有幸预览了 React Native 的 beta 版本,从那开始它大规模成长了起来。

请注意,目前已经支持 iOS。因此你需要运行 OS X 上的 Xcode 来跟随本教程。

If you haven't had a chance to learn React, take a look at my tutorial to get started with it.

It's important to note that this doesn't mean we can write code once  and use it everywhere. Trying to do that can be disastrous as the level  of abstraction would be insane. Instead, React Native allows us to learn  once, write everywhere.

Going back to 2004

If you have watched The Social Network you will remember FaceMash,  the application that kicked off Facebook. For those who haven't watched,  11 years ago (wow) Mark Zuckerberg created FaceMash, an application  where you could view two people and rate which one is hotter. Each  person would have a score (although the original algorithm is unknown,  the movie shows the Elo rating algorithm being used) that reflected how "hot" they are.

Here it is in all it's glory -

 

Let's go full circle - we're going to be recreating FaceMash in React  Native. If you think it's immoral to rate girls on their looks, feel  free to change the pictures to something else that you find attractive  (dogs, snippets of code, etc, I'm not judging you).

如果你还没有过机会学习 React, 看看我的教程 来开始用用它吧.

要重点注意学习这个并不意味着我们可以写一次代码就能将这段代码用到每一个地方。尝试那样做会因为疯狂的抽象级别而陷入一场灾难。React Native 则让我们可以学习一次,到处编写。

回到 2004

如果你关注社交网络领域的话你会记得 FaceMash,正式这个应用开创了 Facebook。对于不关注这个领域的人,其实是11年前(哇塞) Mark Zuckerberg 创建了 FaceMash,它是一个你可以用来查看两个人之中谁更加热门的应用程序。每一个人都有一个能反映他们有多“热门”的分数值 (尽管不知道原来使用的是什么算法,不过那部电影(社交网络)显示 Elo 排名算法 曾被使用过) .

它全部的荣耀都在于此 -

  

让我们整个来过一遍吧 - 我们准备用 React Native 来重新创建 FaceMash。如果你觉得凭外貌来评价姑娘们不道德,你可以把图片变成你觉得能吸引人的其它事务(狗狗,代码块,等等,我不做评价),随便。

Creating our app

You can clone the start repo here if you wish. This isn't necessary, however there different branches of the code at different stages just in case you get lost!

Houston, we have lift off

If you haven't cloned the repo, we need to setup our base project.  React Native allows us to quickly start up a project with the react-native-cli npm package CLI. If you don't have it installed, quickly run

npm install -g react-native-cli

And then we can get started.

Navigate to a folder in the terminal and run

react-native init FaceMash

This will create our base application ready for us to dig in and add to.

Open it up

Launch XCode and navigate to the directory you created the application in. From here, we will need to open up facemash.xcodeproj.

React Native supports us working in both the iOS Simulator and our actual iOS devices.

I will be developing in the iOS Simulator as it allows more rapid application development - we can press Command + R  to refresh the application when we make changes to our JavaScript, or  we can be super lazy and enable live reloading via the developer menu  (accessible via Command + Control + Z). We can even debug our code in the Chrome developer tools.

创建你的应用

如你所愿,你可以从 这儿 clone 到初始的代码库。这不是必须的,不过为了不让你错过不同阶段代码的不同分支,你可以 clone 一份下来!

休斯顿,我们已经升空

如果你没有 clone 代码库,就需要设置基础项目.  React Native 可以让我们使用 react-native-cli npm 包 CLI 快速开始一个项目。如果你还没有安装这个,可以快速运行命令:

npm install -g react-native-cli

然后我们就可以开始了.

在终端里导航到一个文件夹并运行命令:

react-native init FaceMash

这样做能为我们准备好基础到应用程序,供我们挖掘和加入更多东西.

打开它

打开 XCode 并浏览到你创建了应用程序的目录里面. 你需要从这里打开 facemash.xcodeproj。

React Native 支持我们在 iOS 模拟器和实际的 iOS 设备上工作.

我将会在 iOS 模拟器上面进行开发,因为它运行更多快速的应用程序开发 - 当我们修改了JavaScript 时,可以按下 Command + R 组合建来刷新应用,或者我们也可以通过 developer 菜单(通过 Command + Control + Z 就能访问到)启用动态重新载入来变成超级懒人。我们设置可以在Chrome的开发者工具中调试我们的代码。

If you wish to develop the application using your iOS device, you  will need to be on the same network as your computer. By default, React  Native will look at localhost for the JavaScript so you will need to point it to your computer.

We can do this by editing the AppDelegate.m file and changing localhost to our local IP. You can find this on OS X by Alt + Click-ing the wireless menu.

We can now run our application. The application will open in whatever  target you've picked in XCode. When we click run, a terminal process  will also spawn running npm start in the directory of our application. If you don't wish to run the app through XCode, make sure you run npm start. This will create a local web server on port 8081 which points to our compiled JavaScript code and also watch and recompile our code on save.

 

I'm running the application in a simulated iPhone 6, running at a 50% scale.

There we go, we've got a blank canvas with so much room for activities!

    如果你希望使用你的 iOS 设备来开发你的应用程序,就需要让设备痛你的计算机处于同一个网络中。React 默认会在 localhost 找到 JavaScript,所以就需要你将它指向你的计算机.

    我们可以通过编辑 AppDelegate.m 文件,将 localhost 改成我们的本地 IP 来达成这个目的. 你可以通过按下 Alt 的同时点击 wireless 菜单 在 OS X 来找到这个东西.

现在就可以运行我们的应用程序了。应用程序会在你在 XCode 中选择的目标中打开. 当我们点击运行,同时会产生一个在我们应用程序目录中运行着 npm start 的终端线程. 如果你不希望通过 XCode 运行应用,确保你运行了 npm start。这将会创建一个在端口8081上的本地 web 服务器,它指向我们编译好的 JavaScript 代码,并且也会监视到我们保存代码的动作以进行重新编译。

  

我把应用程序运行在一个模拟的 iPhone6 之上, 屏幕是真实设备的50%那么大.

这就是了,我们有了一个空的 canvas,有好多空间活动啊!

Taking a peek

Let's take a look at the code it takes to render what we can see in the screenshot above. Open up index.ios.js.

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 */
'use strict';
var React = require('react-native');
var {
  AppRegistry,
  StyleSheet,
  Text,
  View,
} = React;
var facemash = React.createClass({
  render: function() {
    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>
          Welcome to React Native!
        </Text>
        <Text style={styles.instructions}>
          To get started, edit index.ios.js
        </Text>
        <Text style={styles.instructions}>
          Press Cmd+R to reload,{'\n'}
          Cmd+Control+Z for dev menu
        </Text>
      </View>
    );
  }
});
var styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  instructions: {
    textAlign: 'center',
    color: '#333333',
    marginBottom: 5,
  },
});
AppRegistry.registerComponent('facemash', () => facemash);

You can close your mouth now - yes, that is all it takes to render our application. Looks familiar, doesn't it?

React Native vs the browser

Not all of React Native matches what you would've been using with  React in the browser. However, the differences between the two are so  minuscule there's no need to worry about them.

  • Instead of using block elements such as div or section, we use the View component in React Native. This maps over to the native iOS component UIView.

  • All text must be wrapped inside the Text component

  • No stylesheets - all your styling is written as JavaScript objects

  • We don't need to worry about what is browser compatibility - ES6 harmony is supported out of the box as well as flexbox

悄悄来看一看

让我来看看拿来渲染我们可以在上面的截屏中所看到的东西的代码. 打开 index.ios.js.

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 */
'use strict';
var React = require('react-native');
var {
  AppRegistry,
  StyleSheet,
  Text,
  View,
} = React;
var facemash = React.createClass({
  render: function() {
    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>
          Welcome to React Native!
        </Text>
        <Text style={styles.instructions}>
          To get started, edit index.ios.js
        </Text>
        <Text style={styles.instructions}>
          Press Cmd+R to reload,{'\n'}
          Cmd+Control+Z for dev menu
        </Text>
      </View>
    );
  }
});
var styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  instructions: {
    textAlign: 'center',
    color: '#333333',
    marginBottom: 5,
  },
});
AppRegistry.registerComponent('facemash', () => facemash);

你可以合上你惊讶的嘴了 - 是的,这就是我们拿来渲染我们的应用程序的全部东西。看起来熟悉,对不对?

React Native vs 浏览器

不是 React Native 的所有东西都能满足你在浏览器中使用React的用途. 不过,两者之间的区别是如此的微不足道,所以完全没有必要担心它们.

  • 不使用诸如 div 活着 section 之类的块元素, 我们在React中使用的是View组件. 它会映射到原生的 iOS 组件 UIView.

  • 所有的文本都必须被封装到 Text 组件里面。

  • 没有样式表 - 你的所有的样式都是被写成 JavaScript 对象的。

  • 我们没有必要担心浏览器的兼容性问题 - ES6 harmony 是在盒子之外受到支持的,flexbox也是如此。

Get to work

We're going to start by clearing out the stylesheet and render  function of our React component. Ideally, to get a basic feeling of  React Native, we're going to try and use as many different components as  we can.

Let's start off with the TabBarIOS component. You might recognise the TabBar component, used in some of the core iOS apps such as Clock and Photos.

var React = require('react-native');
var {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  TabBarIOS
} = React;
var facemash = React.createClass({
  getInitialState() {
    return {
      selectedTab: 'faceMash'
    }
  },
  render: function() {
    return (
      <TabBarIOS>
        <TabBarIOS.Item
          title="FaceMash"
          icon={ require('image!facemash') }
          selected={ this.state.selectedTab === 'faceMash' }>
          <View style={ styles.pageView }>
            <Text>Face Mash</Text>
          </View>
        </TabBarIOS.Item>
        <TabBarIOS.Item
          title="Messages"
          icon={ require('image!messages') }
          selected={ this.state.selectedTab === 'messages' }>
          <View style={ styles.pageView }>
            <Text>Messages</Text>
          </View>
        </TabBarIOS.Item>
        <TabBarIOS.Item
          title="Settings"
          icon={ require('image!settings') }
          selected={ this.state.selectedTab === 'settings' }>
          <View style={ styles.pageView }>
            <Text>Settings</Text>
          </View>
        </TabBarIOS.Item>
      </TabBarIOS>
    );
  }
});
var styles = StyleSheet.create({
  pageView: {
    backgroundColor: '#fff',
    flex: 1
  }
});
// omitted code

       

Look at that! You'll notice that the text is currently over the status bar, but don't worry, we'll fix that later on.

The TabBarIOS component uses TabBarIOS.Item  for each of its children. We're going to have three pages - the page  where you rate people, a messages list and a settings page.

The TabBarIOS.Item must have one child. This will be the  contents of the page that has been selected (you can see us setting  selected to true/false depending on the state of the component).

Obviously, a tab bar wouldn't look good without icons. There are a  few system icons you can use, however if you use them the text of the  tab also changes to match the icon. Instead, we can use our own icons.  To import a local image asset in React Native, you can use require and prepend the asset name with image!.

I'm using icons that are available free, under the CC 3.0 licence, from flaticon.

开始工作

我们准备从清理 React 组件的样式表盒渲染函数开始。为了对 React Native 有一个理想的基本了解,我们将尝试使用尽可能多的不同组件。

让我们先从 TabBarIOS 组件开始. 你也许能认出 TabBar 组件来,它被用在诸如时钟和照片这样一些核心的iOS应用中。

var React = require('react-native');
var {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  TabBarIOS
} = React;
var facemash = React.createClass({
  getInitialState() {
    return {
      selectedTab: 'faceMash'
    }
  },
  render: function() {
    return (
      <TabBarIOS>
        <TabBarIOS.Item
          title="FaceMash"
          icon={ require('image!facemash') }
          selected={ this.state.selectedTab === 'faceMash' }>
          <View style={ styles.pageView }>
            <Text>Face Mash</Text>
          </View>
        </TabBarIOS.Item>
        <TabBarIOS.Item
          title="Messages"
          icon={ require('image!messages') }
          selected={ this.state.selectedTab === 'messages' }>
          <View style={ styles.pageView }>
            <Text>Messages</Text>
          </View>
        </TabBarIOS.Item>
        <TabBarIOS.Item
          title="Settings"
          icon={ require('image!settings') }
          selected={ this.state.selectedTab === 'settings' }>
          <View style={ styles.pageView }>
            <Text>Settings</Text>
          </View>
        </TabBarIOS.Item>
      </TabBarIOS>
    );
  }
});
var styles = StyleSheet.create({
  pageView: {
    backgroundColor: '#fff',
    flex: 1
  }
});
// omitted code

       

看看这个!你会注意到当前的文本覆在了状态条上面,我们稍后会修复这个问题。

TabBarIOS 组件对它的每一个子项都使用了 TabBarIOS.Item。我们将会有三个页面——分别是你给人们评级的页面,一个消息列表以及一个设置的页面。

TabBarIOS.Item 必须有一个子项。他将会是已经被选取的页面的内容(你可以发现我们会根据组件的状态来选择设置成true还是false)。

很明显,一个 TAB 条没有图标不会好看。有几个系统图标是你可以拿来用的,不过如果你用了他们的话,TAB 的文字也会发生变化,以与系统的图标配对. 所以我们会使用自己的图标。为了在 React Native 中引入本地的图片资源,你可以使用 require 后面带上图片的资源名称!

我使用的图标是可以免费拿来用的,来自于 flaticon 的 CC 3.0 许可.

Using Static Images

To add a static image for React Native, open up XCode. In the Project Navigator (the first icon on the left hand pane), open up Images.xcassets. All your images will sit here.

This allows us to keep all our assets under one name, whilst  providing different images for both each resolution, and even device  specific images.

Images must follow a strict naming convention. Use the asset name (such as messages or settings)  and append it with the resolution it should be displayed at. For  instance, I'm building my application for an iPhone 6 so I would use the  @2x resolution.

Once you've named your image file correctly, drag and drop it into the left hand page in Images.xcassets.

You can then use require('image!assetname') in React Native!

使用静态图片

为了向 React Native 添加静态图片,请打开 XCode。在 Project Navigator (左手边的第一个图标)中, 打开 Images.xcassets 。你所有的图片都在那儿。

这可以让我们将所有的资源保持在同一个名称下,这样可以针对每一个分辨率、甚至是设备的特定图片提供不同的图像资源。

图像必须遵循一个严格的命名约定。使用的资源名称(比如 messages 或者是 settings) 并在后面给它带上它应该适用来显示的分辨率。例如,我要为 iPhone6 构建一个应用程序,我会为此使用 @2x 分辨率。

一旦为你的图片进行了正确的命名,就可以将它拖入左手边的 Images.xcassets 中了。

然后你就可以在 React Native 中使用 require('image!assetname') 了!

Back to the code

The next logical step is to set up our main component to allow  switching between tabs. We can do this by just setting the state when  the user clicks on it. TabBarIOS.Item allows us to give it an onPress property, which we can use to detect when a user taps on the tab.

// omitted code

var facemash = React.createClass({
  getInitialState() {
    return {
      selectedTab: 'faceMash'
    }
  },
  changeTab(tabName) {
    this.setState({
      selectedTab: tabName
    });
  },
  render: function() {
    return (
      <TabBarIOS>
        <TabBarIOS.Item
          title="FaceMash"
          icon={ require('image!facemash') }
          onPress={ () => this.changeTab('faceMash') }
          selected={ this.state.selectedTab === 'faceMash' }>
          <View style={ styles.pageView }>
            <Text>Face Mash</Text>
          </View>
        </TabBarIOS.Item>
        <TabBarIOS.Item
          title="Messages"
          icon={ require('image!messages') }
          onPress={ () => this.changeTab('messages') }
          selected={ this.state.selectedTab === 'messages' }>
          <View style={ styles.pageView }>
            <Text>Messages</Text>
          </View>
        </TabBarIOS.Item>
        <TabBarIOS.Item
          title="Settings"
          icon={ require('image!settings') }
          onPress={ () => this.changeTab('settings') }
          selected={ this.state.selectedTab === 'settings' }>
          <View style={ styles.pageView }>
            <Text>Settings</Text>
          </View>
        </TabBarIOS.Item>
      </TabBarIOS>
    );
  }
});

// omitted code

       

Done! It's that simple. Refresh your app by pressing Command + R  in the iOS simulator (or recompile it via XCode if you're developing on  your device) and you'll notice we can now tap between tabs, and the  screen changes!

Although we haven't written much code, you can check out the step-one branch, which also contains all the icons we've used for the tabs.

回到代码

下一个逻辑步骤就是设置我们的主组件使得 Tab 之间的切换可用。我们可以通过设置用户点击它时的状态来做到。TabBarIOS.Item 让我们可以给它一个 onPress 属性,可以拿来检测用户何时按下了一个tab。

// omitted code

var facemash = React.createClass({
  getInitialState() {
    return {
      selectedTab: 'faceMash'
    }
  },
  changeTab(tabName) {
    this.setState({
      selectedTab: tabName
    });
  },
  render: function() {
    return (
      <TabBarIOS>
        <TabBarIOS.Item
          title="FaceMash"
          icon={ require('image!facemash') }
          onPress={ () => this.changeTab('faceMash') }
          selected={ this.state.selectedTab === 'faceMash' }>
          <View style={ styles.pageView }>
            <Text>Face Mash</Text>
          </View>
        </TabBarIOS.Item>
        <TabBarIOS.Item
          title="Messages"
          icon={ require('image!messages') }
          onPress={ () => this.changeTab('messages') }
          selected={ this.state.selectedTab === 'messages' }>
          <View style={ styles.pageView }>
            <Text>Messages</Text>
          </View>
        </TabBarIOS.Item>
        <TabBarIOS.Item
          title="Settings"
          icon={ require('image!settings') }
          onPress={ () => this.changeTab('settings') }
          selected={ this.state.selectedTab === 'settings' }>
          <View style={ styles.pageView }>
            <Text>Settings</Text>
          </View>
        </TabBarIOS.Item>
      </TabBarIOS>
    );
  }
});

// omitted code

       

可以了!它是多么的简单. 通过在 iOS 模拟器中按下 Command+R 来刷新应用(或者如果你是在真实设备上开发,可以通过 XCode 来对它进行重新编译) 你就会看到现在我们可以进行按下 tab 的操作了,并且主屏幕的显示也发生了变化!

尽管我们还没有写太多的代码,但是已经见第一个步骤分支的代码 checkout 出来了,里面也包含了我们在这个 tab 上用上了的图标。

Let's Mash

Let's work on the FaceMash tab. We're going to hit an endpoint to load our data using fetch. In the step-one branch, I've included a config.yaml file in the rest/ folder that we're going to use to mock our endpoints using stubby. All the users in the endpoint/pictures were randomly generated at randomuser.me.

Open up your terminal and run

stubby -d rest/config.yaml

And let's get started!

Create a new file in the folder named tabs/ called FaceMash.js and stick a basic React component in there -

'use strict';

var React = require('react-native');

var {
  StyleSheet,
  Text,
  View
  } = React;

var facemashTab = React.createClass({
  render: function() {
    return (
      <View style={ styles.container }>
        <Text>
          FaceMash tab!
        </Text>
      </View>
    );
  }
});

var styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff'
  }
});

module.exports = facemashTab;

All we've got on this tab so far is a basic View component with some text inside of it. We've also got some basic styling for the View, to make sure that it takes up all available height/width.

We're going to add a header, purely for display purposes.

// omitted code

var facemashTab = React.createClass({
  render: function() {
    return (
      <View style={ styles.container }>
        <View style={ styles.header }>
        </View>
        <View style={ styles.content }>
          <Text>
            FaceMash tab!
          </Text>
        </View>
      </View>
    );
  }
});

var styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff'
  },
  header: {
    height: 40,
    background: '#ff0000'
  }
});

module.exports = facemashTab;

       

Now we're talking. However, this looks terrible as the status bar is black. Not to worry, we can change that using the StatusBarIOS API. When the changeTab function is called, we can check if the tab is the FaceMash tab. If it is, we'll set the status bar style to 1 (white), if not we'll set it to 0 (black).

让我们打分吧

让我们来实现 FaceMash 的 tab 界面吧。我们将会从一个端点那里使用获取来加载到数据。在步骤一的分支中,我已经在 rest/ 目录中包含进来了一个 config.yaml 文件,那是我们将会用来使用 stubby 对端点进行模拟的。所有 endpoint/pictures 中的用户都会被从 randomuser.me 处随机的生成。

打开你的终端并且运行命令

stubby -d rest/config.yaml

接着我们就开始吧!

在名为 tabs/ 的目录中创建一个新文件,命名为 FaceMash.js,在里面放一个基础的 React 组件 -

'use strict';

var React = require('react-native');

var {
  StyleSheet,
  Text,
  View
  } = React;

var facemashTab = React.createClass({
  render: function() {
    return (
      <View style={ styles.container }>
        <Text>
          FaceMash tab!
        </Text>
      </View>
    );
  }
});

var styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff'
  }
});

module.exports = facemashTab;

目前我们能从这个Tab上得到全部就是一个里面有一些文字的基础的 View 组件。我们还可以为这个 View 弄一些基础的样式,这样可以确保它具有合适的高和宽。

我们会添加一个头部,纯粹是用于展示的目的.

// omitted code

var facemashTab = React.createClass({
  render: function() {
    return (
      <View style={ styles.container }>
        <View style={ styles.header }>
        </View>
        <View style={ styles.content }>
          <Text>
            FaceMash tab!
          </Text>
        </View>
      </View>
    );
  }
});

var styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff'
  },
  header: {
    height: 40,
    background: '#ff0000'
  }
});

module.exports = facemashTab;

           

现在我们会抱怨状态条的黑色很糟糕,不过不要担心,因为我们可以使用 StatusBarIOS 的 API 来对其进行修改。当 changeTabfunction 被调用时,我们可以检查看看当前的 tab 是不是 FaceMash 的 tab。如果是的话,我们将会把状态调的状态设置为1(白色),如果不是就设置为0(黑色).

index.ios.js

// omitted code

var {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  TabBarIOS,
  StatusBarIOS
} = React;

var facemash = React.createClass({
  ...,
  changeTab(tabName) {
  StatusBarIOS.setStyle(tabName === 'faceMash' ? 1 : 0);
    this.setState({
      selectedTab: tabName
    });
  },
  ...
});

// omitted code

Refresh and you'll see a white status bar - sorted!

We can now get to hitting the endpoint and displaying our users. We'll be using fetch, which is included for us in React Native by default.

// omitted code

var facemashTab = React.createClass({
  getInitialState: function() {
    return {
      list: [],
      currentIndex: 0
    };
  },
  componentWillMount: function() {
    fetch('http://localhost:8882/rest/mash')
      .then(res => res.json())
      .then(res => this.setState({ list: res }));
  },
  render: function() {
    return (
      ...
    );
  }
});

// omitted code

The request will populate our state with the returned data. As the initial state is an empty array, we can check that in our render function and show the user a loading page whilst they wait.

var {
  StyleSheet,
  Text,
  View,
  ActivityIndicatorIOS
  } = React;

var facemashTab = React.createClass({
  ...,
  render: function() {
    var contents;
    if (!this.state.list.length) {
      contents = (
        <View style={ styles.loading }>
          <Text style={ styles.loadingText }>Loading</Text>
          <ActivityIndicatorIOS />
        </View>
      )
    } else {
      contents = (
        <View style={ styles.content }>
          <Text>Loaded</Text>
        </View>
      )
    }
    return (
      <View style={ styles.container }>
        <View style={ styles.header }>
          <Text style={ styles.headerText }>FaceMash</Text>
        </View>
        { contents }
      </View>
    );
  }
});

var styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff'
  },
  loading: {
    flex: 1,
    backgroundColor: '#fff',
    justifyContent: 'center',
    alignItems: 'center'
  },
  loadingText: {
    fontSize: 14,
    marginBottom: 20
  },
  header: {
    height: 50,
    backgroundColor: '#760004',
    paddingTop: 20,
    alignItems: 'center'
  },
  headerText: {
    color: '#fff',
    fontSize: 20,
    fontWeight: 'bold'
  }
});

           

We will now have access to our data in this.state.list.  We've also got what current index of that array we're at in the state as  the endpoint returns an array of objects - each object being two people  that the user can rate.

As there are two people to pick from, both of which have the same  data associated with them, we're going to make a React component that  will display their data.

// omitted code

var Person = React.createClass({
  render: function() {
    var person = this.props.person;
    return (
      <View style={ styles.person }>
        <Text>Person!</Text>
      </View>
    )
  }
});

var facemashTab = React.createClass({
  getInitialState: function() {
    return {
      list: [],
      currentIndex: 0
    };
  },
  componentWillMount: function() {
    fetch('http://localhost:8882/rest/mash')
      .then(res => res.json())
      .then(res => this.setState({ list: res }));
  },
  render: function() {
    var contents;
    if (!this.state.list.length) {
      contents = (
        <View style={ styles.loading }>
          <Text style={ styles.loadingText }>Loading</Text>
          <ActivityIndicatorIOS />
        </View>
      )
    } else {
      var { list, currentIndex } = this.state;
      var record = list[currentIndex];
      var people = record.users.map(person => <Person person={ person } />);
      contents = (
        <View style={ styles.content }>
          { people }
        </View>
      )
    }
    return (
      <View style={ styles.container }>
        <View style={ styles.header }>
          <Text style={ styles.headerText }>FaceMash</Text>
        </View>
        { contents }
      </View>
    );
  }
});

var styles = StyleSheet.create({
  ...,
  person: {
    flex: 1,
    margin: 10,
    borderRadius: 3,
    overflow: 'hidden'
  }
});

We've now got a component that mounts twice (once for each person),  and the appropriate configuration passed through to it. We can now  display the profile picture and the relevant user info.

index.ios.js

// omitted code

var {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  TabBarIOS,
  StatusBarIOS
} = React;

var facemash = React.createClass({
  ...,
  changeTab(tabName) {
  StatusBarIOS.setStyle(tabName === 'faceMash' ? 1 : 0);
    this.setState({
      selectedTab: tabName
    });
  },
  ...
});

// omitted code

刷新你就会看到一个白色的状态条 - 解决了!

我们现在可以访问端点来向我们的用户进行展示了。我们将会使用 fetch,它在 React Native 中默认是被包含了进来的。

// omitted code

var facemashTab = React.createClass({
  getInitialState: function() {
    return {
      list: [],
      currentIndex: 0
    };
  },
  componentWillMount: function() {
    fetch('http://localhost:8882/rest/mash')
      .then(res => res.json())
      .then(res => this.setState({ list: res }));
  },
  render: function() {
    return (
      ...
    );
  }
});

// omitted code

请求会用返回的数据对我们的状态进行填充。因为初始的数据时一个空的数组,所以我们可以在 render 函数中进行检查,在他们等待的时候显示一个加载页面。

var {
  StyleSheet,
  Text,
  View,
  ActivityIndicatorIOS
  } = React;

var facemashTab = React.createClass({
  ...,
  render: function() {
    var contents;
    if (!this.state.list.length) {
      contents = (
        <View style={ styles.loading }>
          <Text style={ styles.loadingText }>Loading</Text>
          <ActivityIndicatorIOS />
        </View>
      )
    } else {
      contents = (
        <View style={ styles.content }>
          <Text>Loaded</Text>
        </View>
      )
    }
    return (
      <View style={ styles.container }>
        <View style={ styles.header }>
          <Text style={ styles.headerText }>FaceMash</Text>
        </View>
        { contents }
      </View>
    );
  }
});

var styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff'
  },
  loading: {
    flex: 1,
    backgroundColor: '#fff',
    justifyContent: 'center',
    alignItems: 'center'
  },
  loadingText: {
    fontSize: 14,
    marginBottom: 20
  },
  header: {
    height: 50,
    backgroundColor: '#760004',
    paddingTop: 20,
    alignItems: 'center'
  },
  headerText: {
    color: '#fff',
    fontSize: 20,
    fontWeight: 'bold'
  }
});

                

现在我们将对位于 this.state.list 的数据进行访问。我们也会在端点返回一个对象的数组时,得到位于状态中的数组的当前索引 - 每个对象都是用户可以进行评比的两个人.

因为要从两个人中选一个,两者都有同自身相关联的相同数据,我们将创建一个 React 组件来展示他们的数据。

// omitted code

var Person = React.createClass({
  render: function() {
    var person = this.props.person;
    return (
      <View style={ styles.person }>
        <Text>Person!</Text>
      </View>
    )
  }
});

var facemashTab = React.createClass({
  getInitialState: function() {
    return {
      list: [],
      currentIndex: 0
    };
  },
  componentWillMount: function() {
    fetch('http://localhost:8882/rest/mash')
      .then(res => res.json())
      .then(res => this.setState({ list: res }));
  },
  render: function() {
    var contents;
    if (!this.state.list.length) {
      contents = (
        <View style={ styles.loading }>
          <Text style={ styles.loadingText }>Loading</Text>
          <ActivityIndicatorIOS />
        </View>
      )
    } else {
      var { list, currentIndex } = this.state;
      var record = list[currentIndex];
      var people = record.users.map(person => <Person person={ person } />);
      contents = (
        <View style={ styles.content }>
          { people }
        </View>
      )
    }
    return (
      <View style={ styles.container }>
        <View style={ styles.header }>
          <Text style={ styles.headerText }>FaceMash</Text>
        </View>
        { contents }
      </View>
    );
  }
});

var styles = StyleSheet.create({
  ...,
  person: {
    flex: 1,
    margin: 10,
    borderRadius: 3,
    overflow: 'hidden'
  }
});

我们现在就有了一个进行两次数据装入(每个人一次)的组件,合适的配置会向它进行传递。现在就可以将个人资料图片和相关的用户信息展示出来了。

返回顶部
顶部