Skip to content

React-Native #15

@Wscats

Description

@Wscats

安装

我们推荐使用Homebrew来安装NodeWatchman。在命令行中执行下列命令安装:

brew install node
brew install watchman

如果你已经安装了 Node,请检查其版本是否在 v8.3 以上。安装完 Node 后建议设置 npm 镜像以加速后面的过程(或使用科学上网工具)。

注意:不要使用 cnpm!cnpm 安装的模块路径比较奇怪,packager 不能正常识别!

npm config set registry https://registry.npm.taobao.org --global
npm config set disturl https://npm.taobao.org/dist --global

Yarn、React Native 的命令行工具(react-native-cli)
Yarn是 Facebook 提供的替代 npm 的工具,可以加速 node 模块的下载。React Native 的命令行工具用于执行创建、初始化、更新项目、运行打包服务(packager)等任务

npm install -g yarn react-native-cli

安装完 yarn 后同理也要设置镜像源:

yarn config set registry https://registry.npm.taobao.org --global
yarn config set disturl https://npm.taobao.org/dist --global

创建项目

使用 React Native 命令行工具来创建一个名为AwesomeProject的新项目:

react-native init AwesomeProject
cd AwesomeProject && npm install

启动

进入ios文件夹用Xcode打开AwesomeProject.xcodeproj文件,并启动
屏幕快照 2019-06-30 上午7 13 52

props

大多数组件在创建时就可以使用各种参数来进行定制。用于定制的这些参数就称为props属性。

class Greeting extends Component {
  render() {
    return (
      <View style={{alignItems: 'center', marginTop: 50}}>
        <Text>Hello {this.props.name}!</Text>
      </View>
    );
  }
}

type Props = {};
export default class App extends Component<Props> {
  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>Welcome1 to React Native!</Text>
        <Text style={styles.instructions}>To get started, edit App.js</Text>
        <Text style={styles.instructions}>{instructions}</Text>
        <Greeting name='Rexxar' />
        <Greeting name='Jaina' />
        <Greeting name='Valeera' />
      </View>
    );
  }
}

布局

在组件样式中使用flex可以使其在可利用的空间中动态地扩张或收缩。一般而言我们会使用flex:1来指定某个组件扩张以撑满所有剩余的空间。如果有多个并列的子组件使用了flex:1,则这些子组件会平分父容器中剩余的空间。如果这些并列的子组件的flex值不一样,则谁的值更大,谁占据剩余空间的比例就更大(即占据剩余空间的比等于并列组件间flex值的比)。

屏幕快照 2019-06-30 下午9 55 37

<View style={{ flex: 1 }}>
    <View style={{ flex: 1, backgroundColor: 'powderblue' }} />
    <View style={{ flex: 2, backgroundColor: 'skyblue' }} />
    <View style={{ flex: 3, backgroundColor: 'steelblue' }} />
</View>

组件

可以新建src/components 文件夹新建Greeting.js,并在App.js中引入import Greeting from './components/Greeting/Greeting.js';,并使用<Greeting name='Eno Yao’ />

import React, { Component } from 'react';
import { Text, View } from 'react-native';
export default class Greeting extends Component {
    render() {
        return (
            <View style={{ alignItems: 'center', marginTop: 50 }}>
                <Text>Hello {this.props.name}!</Text>
            </View>
        );
    }
}

事件

点击这个按钮会调用onPress函数,具体作用就是显示一个 alert 弹出框。

react react-native
onClick onPress
onChange onChangeText
import React, { Component } from 'react';
import { Text, View, Button, Alert } from 'react-native';
export default class Greeting extends Component {
    render() {
        return (
            <View style={{ alignItems: 'center', marginTop: 50 }}>
                <Text>Hello {this.props.name}!</Text>
                <Button
                    onPress={() => {
                        Alert.alert("你点击了按钮!");
                    }}
                    title="点我!"
                />
            </View>
        );
    }
}

处理文本输入

TextInput是一个允许用户输入文本的基础组件。它有一个名为onChangeText的属性,此属性接受一个函数,而此函数会在文本变化时被调用。另外还有一个名为onSubmitEditing的属性,会在文本被提交后(用户按下软键盘上的提交键)调用。

<TextInput
    style={{ height: 40 }}
    placeholder="Type here to translate!"
    onChangeText={(text) => this.setState({ text })}
/>
<TextInput
    // style={{ height: 40 }}
    placeholder="Type here to translate!"
    onChangeText={this.onChangeText.bind(this)}
/>
// js
onChangeText(text) {
    console.log(text)
    this.setState({ text })
}

导航

在你的React Native项目中安装react-navigation这个包

yarn add react-navigation
# or with npm
# npm install --save react-navigation

然后,安装react-native-gesture-handler

yarn add react-native-gesture-handler
# or with npm
# npm install --save react-native-gesture-handler

Link所有的原生依赖,这一步是必须的,之前漏了这一步在这里花费了很多时间

屏幕快照 2019-06-30 上午9 42 41

react-native link react-native-gesture-handler
# or
react-native link

App.js中把代码替换为下面这些

import React, { Component } from 'react';
import { Text, View, Button } from 'react-native';
import { createAppContainer, createStackNavigator, StackActions, NavigationActions } from 'react-navigation'; // Version can be specified in package.json

class HomeScreen extends Component {
  render() {
    return (
      <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
        <Text>Home Screen</Text>
        <Button
          title="Go to Details"
          onPress={() => {
            this.props.navigation.dispatch(StackActions.reset({
              index: 0,
              actions: [
                NavigationActions.navigate({ routeName: 'Details' })
              ],
            }))
          }}
        />
      </View>
    );
  }
}

class DetailsScreen extends Component {
  render() {
    return (
      <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
        <Text>Details Screen</Text>
      </View>
    );
  }
}

const AppNavigator = createStackNavigator({
  Home: {
    screen: HomeScreen,
  },
  Details: {
    screen: DetailsScreen,
  },
}, {
    initialRouteName: 'Home',
  });

export default createAppContainer(AppNavigator);

导航跳转

<Button
  title="Go to Home"
  onPress={() => this.props.navigation.navigate('Home')}
/>

将路由页面封装模块化封装进组件里面

import { createAppContainer, createStackNavigator } from 'react-navigation'; // Version can be specified in package.json

import DetailsScreen from './src/components/DetailsScreen/DetailsScreen'
import HomeScreen from './src/components/HomeScreen/HomeScreen'

const AppNavigator = createStackNavigator({
  Home: {
    screen: HomeScreen,
  },
  Details: {
    screen: DetailsScreen,
  },
}, {
    initialRouteName: 'Home',
});

export default createAppContainer(AppNavigator);

导航栏标题

屏幕快照 2019-06-30 下午8 52 54

class HomeScreen extends React.Component {
  static navigationOptions = {
    title: 'Home',
  };
}

导航栏按钮

屏幕快照 2019-06-30 下午9 08 51

class LogoTitle extends React.Component {
    render() {
        return (
            <Image
                source={require('../../assets/spiro.png')}
                style={{ width: 30, height: 30 }}
            />
        );
    }
}
export default class DetailsScreen extends Component {
    static navigationOptions = {
        title: 'Detail',
        headerTitle: <LogoTitle />,
        headerRight: (
            <Button
                title="菜单"
                onPress={() => alert('This is a button!')}
                title="Info"
                color="#58bc58"
            />
        ),
    };
    // other code
}

底部导航条

Kapture 2019-07-02 at 13 19 46

import { createAppContainer, createStackNavigator, createBottomTabNavigator } from 'react-navigation'; // Version can be specified in package.json

import DetailsScreen from './src/components/DetailsScreen/DetailsScreen'
import HomeScreen from './src/components/HomeScreen/HomeScreen'
import MineScreen from './src/components/MineScreen/MineScreen'


const MineNavigator = createStackNavigator({
  Mine: {
    screen: MineScreen,
  },
  Details: {
    screen: DetailsScreen,
  },
}, {
    initialRouteName: 'Mine',
  });

const HomeNavigator = createStackNavigator({
  Home: {
    screen: HomeScreen,
  },
  Details: {
    screen: DetailsScreen,
  },
}, {
    initialRouteName: 'Home',
  });

const TabNavigator = createBottomTabNavigator({
  Home: HomeNavigator,
  Mine: MineNavigator,
}, {
    tabBarOptions: {
      activeTintColor: 'tomato',
      inactiveTintColor: 'gray',
    },
  });
export default createAppContainer(TabNavigator);

网络

React Native 提供了和 web 标准一致的Fetch API,用于满足开发者访问网络的需求。如果你之前使用过XMLHttpRequest(即俗称的 ajax)或是其他的网络 API,那么 Fetch 用起来将会相当容易上手。

async componentDidMount() {
    try {
      let response = await fetch('https://cnodejs.org/api/v1/topics');
      console.log(response.json());
    } catch (error) {
      console.error(error);
    }
}
// or
async componentDidMount() {
    var request = new XMLHttpRequest();
    request.onreadystatechange = (e) => {
      if (request.readyState !== 4) {
        return;
      }
      if (request.status === 200) {
        console.log('success', request.responseText);
      } else {
        console.warn('error');
      }
    };
    request.open('GET', 'https://cnodejs.org/api/v1/topics');
    request.send();
}

基础组件

Picker

Kapture 2019-06-30 at 17 50 24

本组件可以在iOS和Android上渲染原生的选择器Picker

import React, { Component } from 'react';
import { Text, View, Picker } from 'react-native';
export default class HomeScreen extends Component {
  constructor(props) {
    super(props);
    this.state = {
      language: 'java',
      itemIndex: 0,
      pickers: [{
        label: 'Java',
        value: 'java'
      }, {
        label: 'JavaScript',
        value: 'js'
      }, {
        label: 'Python',
        value: 'py'
      }]
    };

    this.setDate = this.setDate.bind(this);
  }
  render() {
    return (
      <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
        <Picker
          selectedValue={this.state.language}
          style={{ height: 50, width: 100 }}
          onValueChange={(itemValue, itemIndex) => this.setState({ language: itemValue, itemIndex })}>
          {
            this.state.pickers.map((item, index) => {
              return (<Picker.Item key={index} label={item.label} value={item.value} />)
            })
          }
        </Picker>
        <Text>{this.state.language}{this.state.itemIndex}</Text>
      </View>
    );
  }
  setDate(newDate) {
    this.setState({ chosenDate: newDate })
  }
}

ScrollView && FlatList

Kapture 2019-06-30 at 18 30 12

能实现下拉刷新

import React, { Component } from 'react';
import { Text, ScrollView, FlatList, StyleSheet, RefreshControl } from 'react-native';
export default class HomeScreen extends Component {
  constructor(props) {
    super(props);
    this.state = {
      refreshing: false,
    };
  }
  _onRefresh = () => {
    this.setState({ refreshing: true });
    setTimeout(() => {
      this.setState({ refreshing: false });
    }, 2000)
  }
  render() {
    return (
      <ScrollView refreshControl={
        <RefreshControl
          title='下拉刷新'
          titleColor='#58bc58'
          refreshing={this.state.refreshing}
          onRefresh={this._onRefresh}
        />} contentContainerStyle={styles.contentContainer}>
        <Text style={styles.TextStyle}>1</Text>
      </ScrollView>
    );
  }
}
const styles = StyleSheet.create({
  contentContainer: {
    paddingVertical: 20,
    padding: 20
  },
  TextStyle: {
    color: 'red',
    borderStyle: 'solid'
  }
});

Kapture 2019-06-30 at 20 18 21

FlatList更适于长列表数据,且元素个数可以增删。和ScrollView不同的是,FlatList并不立即渲染所有元素,而是优先渲染屏幕上可见的元素。

FlatList组件必须的两个属性是data和renderItem。data是列表的数据源,而renderItem则从数据源中逐个解析数据,然后返回一个设定好格式的组件来渲染。

import React, { Component } from 'react';
import { Text, ScrollView, FlatList, StyleSheet, RefreshControl } from 'react-native';
export default class HomeScreen extends Component {
  constructor(props) {
    super(props);
    this.state = {
      refreshing: false,
      data: []
    };
  }
  async _onRefresh() {
    this.setState({ refreshing: true });
    var self = this;
    var request = new XMLHttpRequest();
    request.onreadystatechange = (e) => {
      if (request.readyState !== 4) {
        return;
      }
      if (request.status === 200) {
        console.log('success', JSON.parse(request.responseText).data);
        self.setState({
          data: this.state.data.concat(JSON.parse(request.responseText).data)
        })
        self.setState({ refreshing: false });
      } else {
        console.warn('error');
      }
    };
    request.open('GET', 'https://cnodejs.org/api/v1/topics');
    request.send();
  }
  async componentDidMount() {
    this._onRefresh();
  }
  render() {
    return (
      <FlatList refreshControl={
        <RefreshControl
          title='下拉刷新'
          titleColor='#58bc58'
          refreshing={this.state.refreshing}
          onRefresh={this._onRefresh.bind(this)}
        />} contentContainerStyle={styles.contentContainer}
        data={this.state.data}
        renderItem={({ item }) => <Text style={styles.item}>{item.title}</Text>}
        keyExtractor={(item) => item.id}
      />
    );
  }
}
const styles = StyleSheet.create({
  contentContainer: {
    paddingVertical: 20,
    padding: 20
  },
  TextStyle: {
    color: 'red',
    borderStyle: 'solid'
  }
});

实现上拉刷新

<FlatList
        onEndReached={this.onEndReached.bind(this)}
        onEndReachedThreshold="0.05"
/>

参考文档

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions