mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Merge branch
This commit is contained in:
+1
-1
@@ -4,7 +4,7 @@ package = JSON.parse(File.read(File.join(__dir__, "package.json")))
|
||||
|
||||
Pod::Spec.new do |s|
|
||||
s.name = "React"
|
||||
s.version = "0.42.0-rc.1"
|
||||
s.version = "0.42.0-rc.2"
|
||||
s.summary = package["description"]
|
||||
s.description = <<-DESC
|
||||
React Native apps are built using the React JS
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
VERSION_NAME=0.42.0-rc.1
|
||||
VERSION_NAME=0.42.0-rc.2
|
||||
GROUP=com.facebook.react
|
||||
|
||||
POM_NAME=ReactNative
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const copyProjectTemplateAndReplace = require('./copyProjectTemplateAndReplace');
|
||||
const execSync = require('child_process').execSync;
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const availableTemplates = {
|
||||
navigation: 'HelloNavigation',
|
||||
};
|
||||
|
||||
function listTemplatesAndExit(newProjectName, options) {
|
||||
if (options.template === true) {
|
||||
// Just listing templates using 'react-native init --template'.
|
||||
// Not creating a new app.
|
||||
// Print available templates and exit.
|
||||
const templateKeys = Object.keys(availableTemplates);
|
||||
if (templateKeys.length === 0) {
|
||||
// Just a guard, should never happen as long availableTemplates
|
||||
// above is defined correctly :)
|
||||
console.log(
|
||||
'There are no templates available besides ' +
|
||||
'the default "Hello World" one.'
|
||||
);
|
||||
} else {
|
||||
console.log(
|
||||
'The available templates are:\n' +
|
||||
templateKeys.join('\n') +
|
||||
'\nYou can use these to create an app based on a template, for example: ' +
|
||||
'you could run: ' +
|
||||
'react-native init ' + newProjectName + ' --template ' + templateKeys[0]
|
||||
);
|
||||
}
|
||||
// Exit 'react-native init'
|
||||
return true;
|
||||
}
|
||||
// Continue 'react-native init'
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param newProjectName For example 'AwesomeApp'.
|
||||
* @param templateKey Template to use, for example 'navigation'.
|
||||
* @param yarnVersion Version of yarn available on the system, or null if
|
||||
* yarn is not available. For example '0.18.1'.
|
||||
*/
|
||||
function createProjectFromTemplate(destPath, newProjectName, templateKey, yarnVersion) {
|
||||
// Expand the basic 'HelloWorld' template
|
||||
copyProjectTemplateAndReplace(
|
||||
path.resolve('node_modules', 'react-native', 'local-cli', 'templates', 'HelloWorld'),
|
||||
destPath,
|
||||
newProjectName
|
||||
);
|
||||
|
||||
if (templateKey !== undefined) {
|
||||
// Keep the files from the 'HelloWorld' template, and overwrite some of them
|
||||
// with the specified project template.
|
||||
// The 'HelloWorld' template contains the native files (these are used by
|
||||
// all templates) and every other template only contains additional JS code.
|
||||
// Reason:
|
||||
// This way we don't have to duplicate the native files in every template.
|
||||
// If we duplicated them we'd make RN larger and risk that people would
|
||||
// forget to maintain all the copies so they would go out of sync.
|
||||
const templateName = availableTemplates[templateKey];
|
||||
if (templateName) {
|
||||
copyProjectTemplateAndReplace(
|
||||
path.resolve(
|
||||
'node_modules', 'react-native', 'local-cli', 'templates', templateName
|
||||
),
|
||||
destPath,
|
||||
newProjectName
|
||||
);
|
||||
} else {
|
||||
throw new Error('Uknown template: ' + templateKey);
|
||||
}
|
||||
|
||||
// Add dependencies:
|
||||
|
||||
// dependencies.json is a special file that lists additional dependencies
|
||||
// that are required by this template
|
||||
const dependenciesJsonPath = path.resolve(
|
||||
'node_modules', 'react-native', 'local-cli', 'templates', templateName, 'dependencies.json'
|
||||
);
|
||||
if (fs.existsSync(dependenciesJsonPath)) {
|
||||
console.log('Adding dependencies for the project...');
|
||||
const dependencies = JSON.parse(fs.readFileSync(dependenciesJsonPath));
|
||||
for (let depName in dependencies) {
|
||||
const depVersion = dependencies[depName];
|
||||
const depToInstall = depName + '@' + depVersion;
|
||||
console.log('Adding ' + depToInstall + '...');
|
||||
if (yarnVersion) {
|
||||
execSync(`yarn add ${depToInstall}`, {stdio: 'inherit'});
|
||||
} else {
|
||||
execSync(`npm install ${depToInstall} --save --save-exact`, {stdio: 'inherit'});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
listTemplatesAndExit,
|
||||
createProjectFromTemplate,
|
||||
};
|
||||
+16
-10
@@ -8,7 +8,10 @@
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const copyProjectTemplateAndReplace = require('../generator/copyProjectTemplateAndReplace');
|
||||
const {
|
||||
listTemplatesAndExit,
|
||||
createProjectFromTemplate,
|
||||
} = require('../generator/templates');
|
||||
const execSync = require('child_process').execSync;
|
||||
const fs = require('fs');
|
||||
const minimist = require('minimist');
|
||||
@@ -23,15 +26,15 @@ const yarn = require('../util/yarn');
|
||||
* @param projectDir Templates will be copied here.
|
||||
* @param argsOrName Project name or full list of custom arguments
|
||||
* for the generator.
|
||||
* @param options Command line options passed from the react-native-cli directly.
|
||||
* E.g. `{ version: '0.43.0', template: 'navigation' }`
|
||||
*/
|
||||
function init(projectDir, argsOrName) {
|
||||
console.log('Setting up new React Native app in ' + projectDir);
|
||||
|
||||
const args = Array.isArray(argsOrName)
|
||||
? argsOrName // argsOrName was e.g. ['AwesomeApp', '--verbose']
|
||||
: [argsOrName].concat(process.argv.slice(4)); // argsOrName was e.g. 'AwesomeApp'
|
||||
|
||||
// args array is e.g. ['AwesomeApp', '--verbose']
|
||||
// args array is e.g. ['AwesomeApp', '--verbose', '--template', 'navigation']
|
||||
if (!args || args.length === 0) {
|
||||
console.error('react-native init requires a project name.');
|
||||
return;
|
||||
@@ -40,7 +43,14 @@ function init(projectDir, argsOrName) {
|
||||
const newProjectName = args[0];
|
||||
const options = minimist(args);
|
||||
|
||||
generateProject(projectDir, newProjectName, options);
|
||||
if (listTemplatesAndExit(newProjectName, options)) {
|
||||
// Just listing templates using 'react-native init --template'
|
||||
// Not creating a new app.
|
||||
return;
|
||||
} else {
|
||||
console.log('Setting up new React Native app in ' + projectDir);
|
||||
generateProject(projectDir, newProjectName, options);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,11 +77,7 @@ function generateProject(destinationRoot, newProjectName, options) {
|
||||
yarn.getYarnVersionIfAvailable() &&
|
||||
yarn.isGlobalCliUsingYarn(destinationRoot);
|
||||
|
||||
copyProjectTemplateAndReplace(
|
||||
path.resolve('node_modules', 'react-native', 'local-cli', 'templates', 'HelloWorld'),
|
||||
destinationRoot,
|
||||
newProjectName
|
||||
);
|
||||
createProjectFromTemplate(destinationRoot, newProjectName, options.template, yarnVersion);
|
||||
|
||||
if (yarnVersion) {
|
||||
console.log('Adding React...');
|
||||
|
||||
@@ -193,7 +193,7 @@ function runOnAllDevices(args, cmd, packageName, adbPath){
|
||||
}
|
||||
|
||||
console.log(chalk.bold(
|
||||
`Building and installing the app on the device (cd android && ${cmd} ${gradleArgs.join(' ')}...`
|
||||
`Building and installing the app on the device (cd android && ${cmd} ${gradleArgs.join(' ')})...`
|
||||
));
|
||||
|
||||
child_process.execFileSync(cmd, gradleArgs, {
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
# App template for new React Native apps
|
||||
|
||||
This is a simple React Native app template which demonstrates a few basics concepts such as navigation between a few screens, ListViews, and handling text input.
|
||||
|
||||
<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"/>
|
||||
|
||||
## Purpose
|
||||
|
||||
The idea is to make it easier for people to get started with React Native. Currently `react-native init` creates a very simple app that contains one screen with static text. Everyone new to React Native then needs to figure out how to do very basic things such as:
|
||||
- Rendering a list of items fetched from a server
|
||||
- Navigating between screens
|
||||
- Handling text input and the software keyboard
|
||||
|
||||
This app serves as a template used by `react-native init` so it is easier for anyone to get up and running quickly by having an app with a few screens and a ListView ready to go.
|
||||
|
||||
### Best practices
|
||||
|
||||
Another purpose of this app is to define best practices such as the folder structure of a standalone React Native app and naming conventions.
|
||||
|
||||
## Not using Redux
|
||||
|
||||
This template intentionally doesn't use Redux. After discussing with a few people who have experience using Redux we concluded that adding Redux to this app targeted at beginners would make the code more confusing, and wouldn't clearly show the benefits of Redux (because the app is too small). There are already a few concepts to grasp - the React component lifecycle, rendeing lists, using async / await, handling the software keyboard. We thought that's the maximum amount of things to learn at once. It's better for everyone to see patterns in their codebase as the app grows and decide for themselves whether and when they need Redux. See also the post [You Might Not Need Redux](https://medium.com/@dan_abramov/you-might-not-need-redux-be46360cf367#.f3q7kq4b3) by [Dan Abramov](https://twitter.com/dan_abramov).
|
||||
|
||||
## Not using Flow (for now)
|
||||
|
||||
Many people are new to React Native, some are new to ES6 and most people will be new to Flow. Therefore we didn't want to introduce all these concepts all at once in a single codebase. However, it might make sense to later introduce a separate version of this template that uses Flow annotations.
|
||||
|
||||
## Provide feedback
|
||||
|
||||
We need your feedback. Do you have a lot of experience building React Native apps? If so, please carefully read the code of the template and if you think something should be done differently, use issues in the repo [mkonicek/AppTemplateFeedback](https://github.com/mkonicek/AppTemplateFeedback) to discuss what should be done differently.
|
||||
|
||||
## How to use the template
|
||||
|
||||
```
|
||||
$ react-native init MyApp --version 0.42.0-rc.2 --template navigation
|
||||
$ cd MyApp
|
||||
$ react-native run-android
|
||||
$ react-native run-ios
|
||||
```
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
/* @flow */
|
||||
|
||||
import React, { PropTypes, Component } from 'react';
|
||||
@@ -10,7 +12,7 @@ import {
|
||||
} from 'react-native';
|
||||
|
||||
type Props = {
|
||||
offset?: number;
|
||||
offset?: number,
|
||||
}
|
||||
|
||||
type State = {
|
||||
@@ -41,7 +43,7 @@ type State = {
|
||||
*/
|
||||
const KeyboardSpacer = () => (
|
||||
Platform.OS === 'ios' ? <KeyboardSpacerIOS /> : null
|
||||
)
|
||||
);
|
||||
|
||||
class KeyboardSpacerIOS extends Component<Props, Props, State> {
|
||||
static propTypes = {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import {
|
||||
Platform,
|
||||
@@ -21,7 +23,7 @@ const Touchable = ({onPress, children}) => {
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<TouchableHighlight onPress={onPress} underlayColor='#ddd'>
|
||||
<TouchableHighlight onPress={onPress} underlayColor="#ddd">
|
||||
{child}
|
||||
</TouchableHighlight>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"react-navigation": "1.0.0-beta.1"
|
||||
}
|
||||
@@ -2,4 +2,4 @@ import { AppRegistry } from 'react-native';
|
||||
|
||||
import MainNavigator from './views/MainNavigator';
|
||||
|
||||
AppRegistry.registerComponent('ChatExample', () => MainNavigator);
|
||||
AppRegistry.registerComponent('HelloWorld', () => MainNavigator);
|
||||
|
||||
@@ -2,4 +2,4 @@ import { AppRegistry } from 'react-native';
|
||||
|
||||
import MainNavigator from './views/MainNavigator';
|
||||
|
||||
AppRegistry.registerComponent('ChatExample', () => MainNavigator);
|
||||
AppRegistry.registerComponent('HelloWorld', () => MainNavigator);
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
'use strict';
|
||||
|
||||
// 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 chatForName = backendStateForLoggedInPerson.chats.find(
|
||||
chat => chat.name === name
|
||||
);
|
||||
if (chatForName) {
|
||||
chatForName.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,24 +1,20 @@
|
||||
import React, { Component } from 'react';
|
||||
import {
|
||||
ListView,
|
||||
Platform,
|
||||
Text,
|
||||
} from 'react-native';
|
||||
'use strict';
|
||||
|
||||
import { TabNavigator } from 'react-navigation';
|
||||
|
||||
import ChatListScreen from './chat/ChatListScreen';
|
||||
import FriendListScreen from './friends/FriendListScreen';
|
||||
import WelcomeScreen from './welcome/WelcomeScreen';
|
||||
|
||||
/**
|
||||
* Screen with tabs shown on app startup.
|
||||
*/
|
||||
const HomeScreenTabNavigator = TabNavigator({
|
||||
Welcome: {
|
||||
screen: WelcomeScreen,
|
||||
},
|
||||
Chats: {
|
||||
screen: ChatListScreen,
|
||||
},
|
||||
Friends: {
|
||||
screen: FriendListScreen,
|
||||
},
|
||||
});
|
||||
|
||||
export default HomeScreenTabNavigator;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* This is an example React Native app demonstrates ListViews, text input and
|
||||
* navigation between a few screens.
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
'use strict';
|
||||
|
||||
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 {
|
||||
|
||||
@@ -29,28 +34,47 @@ 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();
|
||||
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 +82,11 @@ export default class ChatListScreen extends Component {
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
loadingScreen: {
|
||||
backgroundColor: 'white',
|
||||
paddingTop: 8,
|
||||
flex: 1,
|
||||
},
|
||||
listView: {
|
||||
backgroundColor: 'white',
|
||||
},
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
'use strict';
|
||||
|
||||
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 +22,58 @@ 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;
|
||||
}
|
||||
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',
|
||||
@@ -48,10 +86,10 @@ export default class ChatScreen extends Component {
|
||||
myMessage: '',
|
||||
}
|
||||
});
|
||||
this.refs.textInput.clear();
|
||||
this.textInput.clear();
|
||||
}
|
||||
|
||||
myMessageChange = (event) => {
|
||||
onMyMessageChange = (event) => {
|
||||
this.setState({myMessage: event.nativeEvent.text});
|
||||
}
|
||||
|
||||
@@ -63,10 +101,16 @@ export default class ChatScreen extends Component {
|
||||
)
|
||||
|
||||
render() {
|
||||
if (this.state.isLoading) {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<ActivityIndicator />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<ListView
|
||||
ref="listView"
|
||||
dataSource={this.state.dataSource}
|
||||
renderRow={this.renderRow}
|
||||
style={styles.listView}
|
||||
@@ -74,17 +118,17 @@ export default class ChatScreen extends Component {
|
||||
/>
|
||||
<View style={styles.composer}>
|
||||
<TextInput
|
||||
ref='textInput'
|
||||
ref={(textInput) => { this.textInput = textInput; }}
|
||||
style={styles.textInput}
|
||||
placeholder='Type a message...'
|
||||
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,
|
||||
|
||||
+7
-13
@@ -1,18 +1,19 @@
|
||||
'use strict';
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import {
|
||||
Image,
|
||||
Platform,
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
} from 'react-native';
|
||||
|
||||
import ListItem from '../../components/ListItem';
|
||||
import WelcomeText from './WelcomeText';
|
||||
|
||||
export default class FriendListScreen extends Component {
|
||||
export default class WelcomeScreen extends Component {
|
||||
|
||||
static navigationOptions = {
|
||||
title: 'Friends',
|
||||
title: 'Welcome',
|
||||
header: {
|
||||
visible: Platform.OS === 'ios',
|
||||
},
|
||||
@@ -20,7 +21,7 @@ export default class FriendListScreen extends Component {
|
||||
icon: ({ tintColor }) => (
|
||||
<Image
|
||||
// Using react-native-vector-icons works here too
|
||||
source={require('./friend-icon.png')}
|
||||
source={require('./welcome-icon.png')}
|
||||
style={[styles.icon, {tintColor: tintColor}]}
|
||||
/>
|
||||
),
|
||||
@@ -29,19 +30,12 @@ export default class FriendListScreen extends Component {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text>A list of friends here.</Text>
|
||||
</View>
|
||||
<WelcomeText />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
backgroundColor: 'white',
|
||||
flex: 1,
|
||||
padding: 16,
|
||||
},
|
||||
icon: {
|
||||
width: 30,
|
||||
height: 26,
|
||||
@@ -0,0 +1,52 @@
|
||||
'use strict';
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import {
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
} from 'react-native';
|
||||
|
||||
export default class WelcomeText extends Component {
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.welcome}>
|
||||
Welcome to React Native!
|
||||
</Text>
|
||||
<Text style={styles.instructions}>
|
||||
This app shows the basics of navigating between a few screens,
|
||||
working with ListView and handling text input.
|
||||
</Text>
|
||||
<Text style={styles.instructions}>
|
||||
Modify any files to get started. For example try changing the
|
||||
file views/welcome/WelcomeText.android.js.
|
||||
</Text>
|
||||
<Text style={styles.instructions}>
|
||||
Double tap R on your keyboard to reload,{'\n'}
|
||||
Shake or press menu button for dev menu.
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
backgroundColor: 'white',
|
||||
padding: 20,
|
||||
},
|
||||
welcome: {
|
||||
fontSize: 20,
|
||||
textAlign: 'center',
|
||||
margin: 16,
|
||||
},
|
||||
instructions: {
|
||||
textAlign: 'center',
|
||||
color: '#333333',
|
||||
marginBottom: 12,
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,52 @@
|
||||
'use strict';
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import {
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
} from 'react-native';
|
||||
|
||||
export default class WelcomeText extends Component {
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.welcome}>
|
||||
Welcome to React Native!
|
||||
</Text>
|
||||
<Text style={styles.instructions}>
|
||||
This app shows the basics of navigating between a few screens,
|
||||
working with ListView and handling text input.
|
||||
</Text>
|
||||
<Text style={styles.instructions}>
|
||||
Modify any files to get started. For example try changing the
|
||||
file{'\n'}views/welcome/WelcomeText.ios.js.
|
||||
</Text>
|
||||
<Text style={styles.instructions}>
|
||||
Press Cmd+R to reload,{'\n'}
|
||||
Cmd+D or shake for dev menu.
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
backgroundColor: 'white',
|
||||
padding: 20,
|
||||
},
|
||||
welcome: {
|
||||
fontSize: 20,
|
||||
textAlign: 'center',
|
||||
margin: 16,
|
||||
},
|
||||
instructions: {
|
||||
textAlign: 'center',
|
||||
color: '#333333',
|
||||
marginBottom: 12,
|
||||
},
|
||||
});
|
||||
|
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 4.7 KiB |
+5
-5
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-native",
|
||||
"version": "0.42.0-rc.1",
|
||||
"version": "0.42.0-rc.2",
|
||||
"description": "A framework for building native apps using React",
|
||||
"license": "BSD-3-Clause",
|
||||
"repository": {
|
||||
@@ -115,7 +115,7 @@
|
||||
"react-native": "local-cli/wrong-react-native.js"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "~15.4.0-rc.4"
|
||||
"react": "~15.4.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"absolute-path": "^0.0.0",
|
||||
@@ -205,9 +205,9 @@
|
||||
"jest-repl": "18.0.0",
|
||||
"jest-runtime": "18.0.0",
|
||||
"mock-fs": "^3.11.0",
|
||||
"react": "~15.4.0-rc.4",
|
||||
"react-dom": "~15.4.0-rc.4",
|
||||
"react-test-renderer": "~15.4.0-rc.4",
|
||||
"react": "~15.4.1",
|
||||
"react-dom": "~15.4.1",
|
||||
"react-test-renderer": "~15.4.1",
|
||||
"shelljs": "0.6.0",
|
||||
"sinon": "^2.0.0-pre.2"
|
||||
}
|
||||
|
||||
Vendored
+4
-3
@@ -48,6 +48,7 @@ var semver = require('semver');
|
||||
* if you are in a RN app folder
|
||||
* init - to create a new project and npm install it
|
||||
* --verbose - to print logs while init
|
||||
* --template - name of the template to use, e.g. --template navigation
|
||||
* --version <alternative react-native package> - override default (https://registry.npmjs.org/react-native@latest),
|
||||
* package to install, examples:
|
||||
* - "0.22.0-rc1" - A new app will be created using a specific version of React Native from npm repo
|
||||
@@ -129,7 +130,8 @@ if (cli) {
|
||||
' Options:',
|
||||
'',
|
||||
' -h, --help output usage information',
|
||||
' -v, --version output the version number',
|
||||
' -v, --version use a specific version of React Native',
|
||||
' --template use an app template. Use --template to see available templates.',
|
||||
'',
|
||||
].join('\n'));
|
||||
process.exit(0);
|
||||
@@ -264,8 +266,7 @@ function getInstallPackage(rnPackage) {
|
||||
}
|
||||
|
||||
function run(root, projectName, options) {
|
||||
// E.g. '0.38' or '/path/to/archive.tgz'
|
||||
const rnPackage = options.version;
|
||||
const rnPackage = options.version; // e.g. '0.38' or '/path/to/archive.tgz'
|
||||
const forceNpmClient = options.npm;
|
||||
const yarnVersion = (!forceNpmClient) && getYarnVersionIfAvailable();
|
||||
var installCommand;
|
||||
|
||||
Reference in New Issue
Block a user