mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-04-21 19:11:22 +08:00
CLI: Update Navigation app template
Summary: Update the template that will be used by `react-native init --template navigation`. The goal is to make it easier for people to get started by demonstrating a few very basic concepts such as navigation, lists and text input. **Test plan (required)** <img src="https://cloud.githubusercontent.com/assets/346214/22697898/ced66f52-ed4a-11e6-9b90-df6daef43199.gif" alt="Android Example" height="800" style="float: left"/> <img src="https://cloud.githubusercontent.com/assets/346214/22697901/cfeab3e4-ed4a-11e6-8552-d76585317ac2.gif" alt="iOS Example" height="800"/> Closes https://github.com/facebook/react-native/pull/12260 Differential Revision: D4521758 Pulled By: mkonicek fbshipit-source-id: d7d9e481dd3373917ac68ec9169b9ac3267547a9
This commit is contained in:
committed by
Facebook Github Bot
parent
016969df49
commit
4551e9347a
116
local-cli/templates/HelloNavigation/lib/Backend.js
Normal file
116
local-cli/templates/HelloNavigation/lib/Backend.js
Normal file
@@ -0,0 +1,116 @@
|
||||
|
||||
// This file just a dummy example of a HTTP API to talk to the backend.
|
||||
// The state of the "database" that would normally live on the server
|
||||
// is simply held here in memory.
|
||||
|
||||
const backendStateForLoggedInPerson = {
|
||||
chats: [
|
||||
{
|
||||
name: 'Claire',
|
||||
messages: [
|
||||
{
|
||||
name: 'Claire',
|
||||
text: 'I ❤️ React Native!',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'John',
|
||||
messages: [
|
||||
{
|
||||
name: 'John',
|
||||
text: 'I ❤️ React Native!',
|
||||
},
|
||||
],
|
||||
}
|
||||
],
|
||||
};
|
||||
|
||||
/**
|
||||
* Randomly simulate network failures.
|
||||
* It is useful to enable this during development to make sure our app works
|
||||
* in real-world conditions.
|
||||
*/
|
||||
function isNetworkFailure() {
|
||||
const chanceOfFailure = 0; // 0..1
|
||||
return Math.random() < chanceOfFailure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for the other functions in this file.
|
||||
* Simulates a short delay and then returns a provided value or failure.
|
||||
* This is just a dummy example. Normally we'd make a HTTP request,
|
||||
* see http://facebook.github.io/react-native/docs/network.html
|
||||
*/
|
||||
function _makeSimulatedNetworkRequest(getValue) {
|
||||
const durationMs = 400;
|
||||
return new Promise(function (resolve, reject) {
|
||||
setTimeout(function () {
|
||||
if (isNetworkFailure()) {
|
||||
reject(new Error('Network failure'));
|
||||
} else {
|
||||
getValue(resolve, reject);
|
||||
}
|
||||
}, durationMs);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a list of all chats for the logged in person.
|
||||
*/
|
||||
async function fetchChatList() {
|
||||
return _makeSimulatedNetworkRequest((resolve, reject) => {
|
||||
resolve(backendStateForLoggedInPerson.chats.map(chat => chat.name))
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a single chat.
|
||||
*/
|
||||
async function fetchChat(name) {
|
||||
return _makeSimulatedNetworkRequest((resolve, reject) => {
|
||||
resolve(
|
||||
backendStateForLoggedInPerson.chats.find(
|
||||
chat => chat.name === name
|
||||
)
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Send given message to given person.
|
||||
*/
|
||||
async function sendMessage({name, message}) {
|
||||
return _makeSimulatedNetworkRequest((resolve, reject) => {
|
||||
const chat = backendStateForLoggedInPerson.chats.find(
|
||||
chat => chat.name === name
|
||||
);
|
||||
if (chat) {
|
||||
chat.messages.push({
|
||||
name: 'Me',
|
||||
text: message,
|
||||
});
|
||||
resolve();
|
||||
} else {
|
||||
reject(new Error('Uknown person: ' + name));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const Backend = {
|
||||
fetchChatList,
|
||||
fetchChat,
|
||||
sendMessage,
|
||||
};
|
||||
|
||||
export default Backend;
|
||||
|
||||
// In case you are looking into using Redux for state management,
|
||||
// this is how network requests are done in the f8 app which uses Redux:
|
||||
// - To load some data, a Component fires a Redux action, such as loadSession()
|
||||
// - That action makes the HTTP requests and then dispatches a redux action
|
||||
// {type: 'LOADED_SESSIONS', results}
|
||||
// - Then all reducers get called and one of them updates a part of the application
|
||||
// state by storing the results
|
||||
// - Redux re-renders the connected Components
|
||||
// See https://github.com/fbsamples/f8app/search?utf8=%E2%9C%93&q=loaded_sessions
|
||||
@@ -1,9 +1,4 @@
|
||||
import React, { Component } from 'react';
|
||||
import {
|
||||
ListView,
|
||||
Platform,
|
||||
Text,
|
||||
} from 'react-native';
|
||||
import { TabNavigator } from 'react-navigation';
|
||||
|
||||
import ChatListScreen from './chat/ChatListScreen';
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
import React, { Component } from 'react';
|
||||
import {
|
||||
ActivityIndicator,
|
||||
Image,
|
||||
ListView,
|
||||
Platform,
|
||||
StyleSheet,
|
||||
View,
|
||||
} from 'react-native';
|
||||
import ListItem from '../../components/ListItem';
|
||||
import Backend from '../../lib/Backend';
|
||||
|
||||
export default class ChatListScreen extends Component {
|
||||
|
||||
static navigationOptions = {
|
||||
title: 'Friends',
|
||||
title: 'Chats',
|
||||
header: {
|
||||
visible: Platform.OS === 'ios',
|
||||
},
|
||||
@@ -29,28 +32,48 @@ export default class ChatListScreen extends Component {
|
||||
super(props);
|
||||
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
|
||||
this.state = {
|
||||
dataSource: ds.cloneWithRows([
|
||||
'Claire', 'John'
|
||||
])
|
||||
isLoading: true,
|
||||
dataSource: ds,
|
||||
};
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
const chatList = await Backend.fetchChatList();
|
||||
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
|
||||
this.setState((prevState) => ({
|
||||
dataSource: prevState.dataSource.cloneWithRows(chatList),
|
||||
isLoading: false,
|
||||
}));
|
||||
}
|
||||
|
||||
// Binding the function so it can be passed to ListView below
|
||||
// and 'this' works properly inside _renderRow
|
||||
_renderRow = (name) => {
|
||||
// and 'this' works properly inside renderRow
|
||||
renderRow = (name) => {
|
||||
return (
|
||||
<ListItem
|
||||
label={name}
|
||||
onPress={() => this.props.navigation.navigate('Chat', {name: name})}
|
||||
onPress={() => {
|
||||
// Start fetching in parallel with animating
|
||||
this.props.navigation.navigate('Chat', {
|
||||
name: name,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.isLoading) {
|
||||
return (
|
||||
<View style={styles.loadingScreen}>
|
||||
<ActivityIndicator />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<ListView
|
||||
dataSource={this.state.dataSource}
|
||||
renderRow={this._renderRow}
|
||||
renderRow={this.renderRow}
|
||||
style={styles.listView}
|
||||
/>
|
||||
);
|
||||
@@ -58,6 +81,11 @@ export default class ChatListScreen extends Component {
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
loadingScreen: {
|
||||
backgroundColor: 'white',
|
||||
paddingTop: 8,
|
||||
flex: 1,
|
||||
},
|
||||
listView: {
|
||||
backgroundColor: 'white',
|
||||
},
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import React, { Component } from 'react';
|
||||
import {
|
||||
ActivityIndicator,
|
||||
Button,
|
||||
ListView,
|
||||
Platform,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TextInput,
|
||||
View,
|
||||
} from 'react-native';
|
||||
import KeyboardSpacer from '../../components/KeyboardSpacer';
|
||||
import Backend from '../../lib/Backend';
|
||||
|
||||
export default class ChatScreen extends Component {
|
||||
|
||||
@@ -19,23 +20,59 @@ export default class ChatScreen extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
|
||||
const messages = [
|
||||
{
|
||||
name: props.navigation.state.params.name,
|
||||
name: 'Claire',
|
||||
text: 'I ❤️ React Native!',
|
||||
},
|
||||
];
|
||||
this.state = {
|
||||
messages: messages,
|
||||
dataSource: ds.cloneWithRows(messages),
|
||||
messages: [],
|
||||
dataSource: ds,
|
||||
myMessage: '',
|
||||
isLoading: true,
|
||||
};
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
let chat;
|
||||
try {
|
||||
chat = await Backend.fetchChat(this.props.navigation.state.params.name);
|
||||
} catch (err) {
|
||||
// Here we would handle the fact the request failed, e.g.
|
||||
// set state to display "Messages could not be loaded".
|
||||
// We should also check network connection first before making any
|
||||
// network requests - maybe we're offline? See React Native's NetInfo
|
||||
// module.
|
||||
this.setState({
|
||||
isLoading: false,
|
||||
});
|
||||
return;
|
||||
}
|
||||
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
|
||||
this.setState((prevState) => ({
|
||||
messages: chat.messages,
|
||||
dataSource: prevState.dataSource.cloneWithRows(chat.messages),
|
||||
isLoading: false,
|
||||
}));
|
||||
}
|
||||
|
||||
onAddMessage = async () => {
|
||||
// Optimistically update the UI
|
||||
this.addMessageLocal();
|
||||
// Send the request
|
||||
try {
|
||||
await Backend.sendMessage({
|
||||
name: this.props.navigation.state.params.name,
|
||||
// TODO Is reading state like this outside of setState OK?
|
||||
// Can it contain a stale value?
|
||||
message: this.state.myMessage,
|
||||
});
|
||||
} catch (err) {
|
||||
// Here we would handle the request failure, e.g. call setState
|
||||
// to display a visual hint showing the message could not be sent.
|
||||
}
|
||||
}
|
||||
|
||||
addMessage = () => {
|
||||
addMessageLocal = () => {
|
||||
this.setState((prevState) => {
|
||||
if (!prevState.myMessage) return prevState;
|
||||
if (!prevState.myMessage) {
|
||||
return prevState;
|
||||
}
|
||||
const messages = [
|
||||
...prevState.messages, {
|
||||
name: 'Me',
|
||||
@@ -51,7 +88,7 @@ export default class ChatScreen extends Component {
|
||||
this.refs.textInput.clear();
|
||||
}
|
||||
|
||||
myMessageChange = (event) => {
|
||||
onMyMessageChange = (event) => {
|
||||
this.setState({myMessage: event.nativeEvent.text});
|
||||
}
|
||||
|
||||
@@ -63,6 +100,13 @@ export default class ChatScreen extends Component {
|
||||
)
|
||||
|
||||
render() {
|
||||
if (this.state.isLoading) {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<ActivityIndicator />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<ListView
|
||||
@@ -78,13 +122,13 @@ export default class ChatScreen extends Component {
|
||||
style={styles.textInput}
|
||||
placeholder='Type a message...'
|
||||
text={this.state.myMessage}
|
||||
onSubmitEditing={this.addMessage}
|
||||
onChange={this.myMessageChange}
|
||||
onSubmitEditing={this.onAddMessage}
|
||||
onChange={this.onMyMessageChange}
|
||||
/>
|
||||
{this.state.myMessage !== '' && (
|
||||
<Button
|
||||
title="Send"
|
||||
onPress={this.addMessage}
|
||||
onPress={this.onAddMessage}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
@@ -99,7 +143,6 @@ const styles = StyleSheet.create({
|
||||
flex: 1,
|
||||
padding: 8,
|
||||
backgroundColor: 'white',
|
||||
alignItems: 'flex-end',
|
||||
},
|
||||
listView: {
|
||||
flex: 1,
|
||||
|
||||
@@ -4,7 +4,6 @@ import {
|
||||
Platform,
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
} from 'react-native';
|
||||
|
||||
import ListItem from '../../components/ListItem';
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import React, { Component } from 'react';
|
||||
import {
|
||||
AppRegistry,
|
||||
StyleSheet,
|
||||
Text,
|
||||
View
|
||||
View,
|
||||
} from 'react-native';
|
||||
|
||||
export default class WelcomeText extends Component {
|
||||
@@ -22,8 +21,8 @@ export default class WelcomeText extends Component {
|
||||
file views/welcome/WelcomeText.android.js.
|
||||
</Text>
|
||||
<Text style={styles.instructions}>
|
||||
Press Cmd+R to reload,{'\n'}
|
||||
Cmd+D or shake for dev menu.
|
||||
Double tap R on your keyboard to reload,{'\n'}
|
||||
Shake or press menu button for dev menu.
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import React, { Component } from 'react';
|
||||
import {
|
||||
AppRegistry,
|
||||
StyleSheet,
|
||||
Text,
|
||||
View
|
||||
View,
|
||||
} from 'react-native';
|
||||
|
||||
export default class WelcomeText extends Component {
|
||||
|
||||
Reference in New Issue
Block a user