Update README; add guides to docs

This commit is contained in:
Nicolas Gallagher
2016-02-17 00:40:55 -08:00
parent 3292ced765
commit f8554ecc1e
11 changed files with 355 additions and 176 deletions
+59 -138
View File
@@ -2,114 +2,56 @@
[![Build Status][travis-image]][travis-url] [![Build Status][travis-image]][travis-url]
[![npm version][npm-image]][npm-url] [![npm version][npm-image]][npm-url]
![gzipped size](https://img.shields.io/badge/gzipped-~19.3k-blue.svg) ![gzipped size](https://img.shields.io/badge/gzipped-~22.6k-blue.svg)
[React Native][react-native-url] components and APIs for the Web. Flexbox [React Native][react-native-url] components and APIs for the Web.
layout and JavaScript styling.
* [Discord: #react-native-web on reactiflux][discord-url]
* [Gitter: react-native-web][gitter-url]
## Table of contents
* [Quick start](#quick-start)
* [Overview](#overview)
* [Example](#example)
* [APIs](#apis)
* [Components](#components)
* [Contributing](#contributing)
* [Thanks](#thanks)
* [License](#license)
## Quick start ## Quick start
You can [try the latest version on CodePen](http://codepen.io/necolas/pen/PZzwBR).
To install in your app: To install in your app:
``` ```
npm install --save react@0.14 react-dom@0.14 react-native-web npm install --save react@0.14 react-dom@0.14 react-native-web
``` ```
Or [try it on CodePen](http://codepen.io/necolas/pen/PZzwBR).
Browser support: Chrome, Firefox, Safari >= 7, IE 10, Edge.
## Overview ## Overview
### Importing This is a web implementation of React Native components and APIs. The React
Native components are good web application building blocks, and provide a common
foundation for component libraries.
All API's, components, and a Web-specific `React` are provided by the For example, the [`View`](docs/apis/View.md) component makes it easy to build
`react-native-web` module: common layouts with flexbox, such as stacked and nested boxes with margin and
padding. And the [`StyleSheet`](docs/guides/style.md) API converts styles
```js defined in JavaScript to "atomic" CSS.
import React, { Image, StyleSheet, Text, View } from 'react-native-web'
```
### Client-side rendering
Client-side rendering requires that you use the module's `React` export.
`React.render` is a thin wrapper around `ReactDOM.render` that renders your
application and the style sheet. Styles are updated if new bundles are loaded
asynchronously.
```js
// client.js
import App from './components/App'
import React from 'react-native-web'
React.render(<App />, document.getElementById('react-root'))
```
### Server-side rendering
Server-side rendering is done by calling `React.renderToString` or
`React.renderToStaticMarkup`, the output of both includes the style sheet.
```js
// server.js
import App from './components/App'
import React from 'react-native-web'
const html = React.renderToString(<App />);
const Html = () => (
<html>
<head>
<meta charSet="utf-8" />
<meta content="initial-scale=1,width=device-width" name="viewport" />
</head>
<body>
<div id="react-root" dangerouslySetInnerHTML={{ __html: html }} />
</body>
</html>
)
```
### Styling
React Native for Web allows you to [define styles using
JavaScript](docs/guides/style.md), either with inline styles or
[`StyleSheet.create`](docs/apis/StyleSheet.md).
The `View` component makes it easy to build common layouts with flexbox, such
as stacked and nested boxes with margin and padding. See this [guide to
flexbox][flexbox-guide-url].
### Accessibility
The most common and best supported [accessibility
features](docs/guides/accessibility.md) of the Web are leveraged through 4
props available on most components: `accessible`, `accessibilityLabel`,
`accessibilityLiveRegion`, and `accessibilityRole`.
## Example ## Example
More examples can be found in the [`examples` directory](examples). More examples can be found in the [`examples` directory](examples).
```js ```js
import React, { Image, StyleSheet, Text, View } from 'react-native-web' import React, { AppRegistry, Image, StyleSheet, Text, View } from 'react-native'
// Components
const Card = ({ children }) => <View style={styles.card}>{children}</View> const Card = ({ children }) => <View style={styles.card}>{children}</View>
const Title = ({ children }) => <Text style={styles.title}>{children}</Text> const Title = ({ children }) => <Text style={styles.title}>{children}</Text>
const Photo = ({ uri }) => <Image source={{ uri }} style={styles.image} /> const Photo = ({ uri }) => <Image source={{ uri }} style={styles.image} />
const App = () => (
<Card>
<Title>App Card</Title>
<Photo uri="/some-photo.jpg" />
</Card>
)
// App registration and rendering
AppRegistry.registerComponent('MyApp', () => App)
AppRegistry.runApplication('MyApp', { rootTag: document.getElementById('react-root') })
// Styles
const styles = StyleSheet.create({ const styles = StyleSheet.create({
card: { card: {
flexGrow: 1, flexGrow: 1,
@@ -121,73 +63,52 @@ const styles = StyleSheet.create({
}, },
image: { image: {
height: 40, height: 40,
marginRight: 10, marginVertical: 10,
width: 40 width: 40
} }
}) })
``` ```
## APIs ## Documentation
### [`StyleSheet`](docs/apis/StyleSheet.md) Guides:
StyleSheet is a style abstraction that transforms inline styles to CSS on the * [Accessibility](docs/guides/accessibility.md)
client or the server. It provides a minimal CSS reset targeting elements and * [Client and server rendering](docs/guides/rendering.md)
pseudo-elements beyond the reach of React inline styles. * [Direct manipulation](docs/guides/direct-manipulation.md)
* [Known issues](docs/guides/known-issues.md)
* [React Native](docs/guides/react-native.md)
* [Style](docs/guides/style.md)
## Components Exported modules:
### [`Image`](docs/components/Image.md) * Components
* [`ActivityIndicator`](docs/components/ActivityIndicator.md)
An accessibile image component with support for image resizing, default image, * [`Image`](docs/components/Image.md)
and child content. * [`ListView`](docs/components/ListView.md)
* [`Portal`](docs/components/Portal.md)
### [`ListView`](docs/components/ListView.md) * [`ScrollView`](docs/components/ScrollView.md)
* [`Text`](docs/components/Text.md)
(TODO) * [`TextInput`](docs/components/TextInput.md)
* [`TouchableHighlight`](docs/components/TouchableHighlight.md)
### [`ScrollView`](docs/components/ScrollView.md) * [`TouchableOpacity`](docs/components/TouchableOpacity.md)
* [`TouchableWithoutFeedback`](docs/components/TouchableWithoutFeedback.md)
A scrollable view with event throttling. * [`View`](docs/components/View.md)
* APIs
### [`Text`](docs/components/Text.md) * [`AppRegistry`](docs/apis/AppRegistry.md)
* [`AppState`](docs/apis/AppState.md)
Displays text inline and supports basic press handling. * [`AsyncStorage`](docs/apis/AsyncStorage.md)
* [`Dimensions`](docs/apis/Dimensions.md)
### [`TextInput`](docs/components/TextInput.md) * [`NativeMethods`](docs/apis/NativeMethods.md)
* [`NetInfo`](docs/apis/NetInfo.md)
Accessible single- and multi-line text input via a keyboard. * [`PixelRatio`](docs/apis/PixelRatio.md)
* [`Platform`](docs/apis/Platform.md)
### [`Touchable`](docs/components/Touchable.md) * [`StyleSheet`](docs/apis/StyleSheet.md)
Touch bindings for press and long press.
### [`View`](docs/components/View.md)
The fundamental UI building block using flexbox for layout.
## Contributing
Please read the [contribution guidelines][contributing-url]. Contributions are
welcome!
## Thanks
Thanks to current and past members of the React and React Native teams (in
particular Vjeux and Pete Hunt).
Thanks to [react-tappable](https://github.com/JedWatson/react-tappable) for
backing the current implementation of `Touchable`.
## License ## License
Copyright (c) 2015 Nicolas Gallagher. Released under the [MIT React Native for Web is [BSD licensed](LICENSE).
license](http://www.opensource.org/licenses/mit-license.php).
[contributing-url]: https://github.com/necolas/react-native-web/blob/master/CONTRIBUTING.md
[discord-url]: http://join.reactiflux.com
[flexbox-guide-url]: https://css-tricks.com/snippets/css/a-guide-to-flexbox/
[gitter-url]: https://gitter.im/necolas/react-native-web
[npm-image]: https://badge.fury.io/js/react-native-web.svg [npm-image]: https://badge.fury.io/js/react-native-web.svg
[npm-url]: https://npmjs.org/package/react-native-web [npm-url]: https://npmjs.org/package/react-native-web
[react-native-url]: https://facebook.github.io/react-native/ [react-native-url]: https://facebook.github.io/react-native/
+6 -1
View File
@@ -27,5 +27,10 @@ module.exports = {
}), }),
new webpack.optimize.DedupePlugin(), new webpack.optimize.DedupePlugin(),
new webpack.optimize.OccurenceOrderPlugin() new webpack.optimize.OccurenceOrderPlugin()
] ],
resolve: {
alias: {
'react-native': '../../src'
}
}
} }
+4 -4
View File
@@ -27,10 +27,10 @@ Remove a handler by passing the change event `type` and the `handler`.
## Examples ## Examples
To see the current state, you can check `AppStateIOS.currentState`, which will To see the current state, you can check `AppState.currentState`, which will be
be kept up-to-date. This example will only ever appear to say "Current state kept up-to-date. This example will only ever appear to say "Current state is:
is: active" because the app is only visible to the user when in the `active` active" because the app is only visible to the user when in the `active` state,
state, and the null state will happen only momentarily. and the null state will happen only momentarily.
```js ```js
class Example extends React.Component { class Example extends React.Component {
+9 -7
View File
@@ -9,12 +9,13 @@ The most common and best supported accessibility features of the Web are
exposed as the props: `accessible`, `accessibilityLabel`, exposed as the props: `accessible`, `accessibilityLabel`,
`accessibilityLiveRegion`, and `accessibilityRole`. `accessibilityLiveRegion`, and `accessibilityRole`.
React Native for Web does not provide a way to directly control the rendered React Native for Web does not provide a way to directly control the type of the
HTML element. The `accessibilityRole` prop is used to infer an [analogous HTML rendered HTML element. The `accessibilityRole` prop is used to infer an
element][html-aria-url] to use in addition, where possible. While this may [analogous HTML element][html-aria-url] to use in addition to the resulting
contradict some ARIA recommendations, it also helps avoid certain HTML5 ARIA `role`, where possible. While this may contradict some ARIA
conformance errors and accessibility anti-patterns (e.g., giving a `heading` recommendations, it also helps avoid certain HTML5 conformance errors and
role to a `button` element). accessibility anti-patterns (e.g., giving a `heading` role to a `button`
element).
For example: For example:
@@ -24,7 +25,8 @@ For example:
* `<Text accessibilityRole='link' href='/' />` => `<a role='link' href='/' />`. * `<Text accessibilityRole='link' href='/' />` => `<a role='link' href='/' />`.
* `<View accessibilityRole='main' />` => `<main role='main' />`. * `<View accessibilityRole='main' />` => `<main role='main' />`.
See the component documentation for more details. Other ARIA properties should be set via [direct
manipulation](./direct-manipulation.md).
[aria-in-html-url]: https://w3c.github.io/aria-in-html/ [aria-in-html-url]: https://w3c.github.io/aria-in-html/
[html-accessibility-url]: http://www.html5accessibility.com/ [html-accessibility-url]: http://www.html5accessibility.com/
+115
View File
@@ -0,0 +1,115 @@
# Direct manipulation
It is sometimes necessary to make changes directly to a component without using
state/props to trigger a re-render of the entire subtree in the browser, this
is done by directly modifying a DOM node. `setNativeProps` is the React Native
equivalent to setting properties directly on a DOM node. Use direct
manipulation when frequent re-rendering creates a performance bottleneck Direct
manipulation will not be a tool that you reach for frequently.
## `setNativeProps` and `shouldComponentUpdate`
`setNativeProps` is imperative and stores state in the native layer (DOM,
UIView, etc.) and not within your React components, which makes your code more
difficult to reason about. Before you use it, try to solve your problem with
`setState` and `shouldComponentUpdate`.
## Avoiding conflicts with the render function
If you update a property that is also managed by the render function, you might
end up with some unpredictable and confusing bugs because anytime the component
re-renders and that property changes, whatever value was previously set from
`setNativeProps` will be completely ignored and overridden.
## Why use `setNativeProps` on Web?
Using `setNativeProps` in web-specific code is required when making changes to
`className` or `style`, as these properties are controlled by React Native for
Web and setting them directly may cause unintended rendering issues.
```js
setOpacityTo(value) {
this._childElement.setNativeProps({
style: { opacity: value }
})
}
```
## Composite components and `setNativeProps`
Composite components are not backed by a DOM node, so you cannot call
`setNativeProps` on them. Consider this example:
```js
const MyButton = (props) => (
<View>
<Text>{props.label}</Text>
</View>
)
const App = () => (
<TouchableOpacity>
<MyButton label="Press me!" />
</TouchableOpacity>
)
```
If you run this you will immediately see this error: `Touchable` child must
either be native or forward `setNativeProps` to a native component. This occurs
because `MyButton` isn't directly backed by a native view whose opacity should
be set. You can think about it like this: if you define a component with
`React.Component/createClass` you would not expect to be able to set a style
prop on it and have that work - you would need to pass the style prop down to a
child, unless you are wrapping a native component. Similarly, we are going to
forward `setNativeProps` to a native-backed child component.
## Forward `setNativeProps` to a child
All we need to do is provide a `setNativeProps` method on our component that
calls `setNativeProps` on the appropriate child with the given arguments.
```js
class MyButton extends React.Component {
setNativeProps(nativeProps) {
this._root.setNativeProps(nativeProps)
}
render() {
return (
<View ref={component => this._root = component}>
<Text>{this.props.label}</Text>
</View>
)
}
}
```
You can now use `MyButton` inside of `TouchableOpacity`!
## `setNativeProps` to clear `TextInput` value
Another very common use case of `setNativeProps` is to clear the value of a
`TextInput`. For example, the following code demonstrates clearing the input
when you tap a button:
```js
class App extends React.Component {
_handlePress() {
this._textInput.setNativeProps({ text: '' })
}
render() {
return (
<View style={styles.container}>
<TextInput
ref={component => this._textInput = component}
style={styles.textInput}
/>
<TouchableOpacity onPress={this._handlePress.bind(this)}>
<Text>Clear text</Text>
</TouchableOpacity>
</View>
)
}
}
```
+28
View File
@@ -0,0 +1,28 @@
# Known issues
## Missing modules and Views
This is an initial release of React Native for Web, therefore, not all of the
views present on iOS/Android are released on Web. We are very much interested in
the community's feedback on the next set of modules and views.
Not all the modules or views for iOS/Android can be implemented on Web. In some
cases it will be necessary to use a Web counterpart.
## Missing component props
There are properties that do not work across all platforms. All web-specific
props are annotated with `(web)` in the documentaiton.
## Platform parity
There are some known issues in React Native where APIs could be made more
consistent between platforms. For example, React Native for Web includes
`ActivityIndicator` and a horizontal `ProgressBar`.
Other parts of React Native, such as the `Animated` and `PanResponder` APIs,
are highly complex and have not yet been ported to React Native for Web. Given
the difficulties keeping these APIs in sync with React Native, we'd prefer the
APIs to be published as separate npm packages. If not, we will consider a web
implementation, possibly using the [Web Animations
API/polyfill](https://github.com/web-animations/web-animations-js)
+40
View File
@@ -0,0 +1,40 @@
# React Native
This is an experimental feature to support: using community-developed React
Native components on the Web; and rendering React Native apps to Web.
Use a module loader that supports package aliases (e.g., webpack), and alias
`react-native` to `react-native-web`.
```js
// webpack.config.js
module.exports = {
resolve: {
alias: {
'react-native': 'react-native-web'
}
}
}
```
Web-specific implementations can use the `*.web.js` naming pattern, which
webpack will resolve.
Minor platform differences can use the `Platform` module.
```js
import { AppRegistry, Platform, StyleSheet } from 'react-native'
const styles = StyleSheet.create({
height: (Platform.OS === 'web') ? 200 : 100
})
AppRegistry.registerComponent('MyApp', () => MyApp)
if (Platform.OS === 'web') {
AppRegistry.runApplication('MyApp', {
rootTag: document.getElementById('react-root')
});
}
```
+57
View File
@@ -0,0 +1,57 @@
# Client and Server rendering
## Client-side rendering
```js
// client.js
import React, { AppRegistry } from 'react-native'
import MyApp from './MyApp'
// register the app
AppRegistry.registerApp('MyApp', () => MyApp)
// mount the app within the `rootTag` and run it
AppRegistry.runApplication('MyApp', { initialProps, rootTag: document.getElementById('react-root') })
// DOM render
React.render(<div />, document.getElementById('sidebar-app'))
// Server render
React.renderToString(<div />)
```
## Server-side rendering
Pre-rendering React apps on the server is a key feature for Web applications.
React Native for Web extends `AppRegistry` to provide support for server-side
rendering.
```js
// server.js
import React, { AppRegistry } from 'react-native'
import MyApp from './MyApp'
// register the app
AppRegistry.registerApp('MyApp', () => MyApp)
// prerender the app
const { html, style } = AppRegistry.prerenderApplication('MyApp', { initialProps })
// construct full page markup
const HtmlShell = (html, style) => (
<html>
<head>
<meta charSet="utf-8" />
<meta content="initial-scale=1,width=device-width" name="viewport" />
{style}
</head>
<body>
<div id="react-root" dangerouslySetInnerHTML={{ __html: html }} />
</body>
</html>
)
React.renderToStaticMarkup(<HtmlShell html={html} style={style} />)
```
+21 -16
View File
@@ -42,7 +42,7 @@ documentation of individual components.
## Using styles ## Using styles
All the core components accept a `style` attribute. All the React Native components accept a `style` attribute.
```js ```js
<Text style={styles.text} /> <Text style={styles.text} />
@@ -52,20 +52,27 @@ All the core components accept a `style` attribute.
A common pattern is to conditionally add style based on a condition: A common pattern is to conditionally add style based on a condition:
```js ```js
// either
<View style={{ <View style={{
...styles.base, ...styles.base,
...(this.state.active && styles.active) ...(this.state.active && styles.active)
}} /> }} />
// or
<View style={[
styles.base,
this.state.active && styles.active
]} />
``` ```
## Composing styles ## Composing styles
In order to let a call site customize the style of your component children, you In order to let a call site customize the style of your component children, you
can pass styles around. Use `View.propTypes.style` and `Text.propTypes.style` in can pass styles around. Use `View.propTypes.style` and `Text.propTypes.style` in
order to make sure only styles are being passed. order to make sure only valid styles are being passed.
```js ```js
export default class List extends React.Component { class List extends React.Component {
static propTypes = { static propTypes = {
style: View.propTypes.style, style: View.propTypes.style,
elementStyle: View.propTypes.style, elementStyle: View.propTypes.style,
@@ -108,11 +115,11 @@ class List extends React.Component {
return ( return (
<View <View
children={children} children={children}
style={{ style={[
...this.props.style, this.props.style,
// override border-color when scrolling // override border-color when scrolling
...(isScrolling && { borderColor: 'transparent' }) isScrolling && { borderColor: 'transparent' }
}} ]}
/> />
) )
} }
@@ -125,19 +132,17 @@ class List extends React.Component {
it does not concern itself with _where_ or _when_ those styles are applied to it does not concern itself with _where_ or _when_ those styles are applied to
elements. elements.
Changing styles in response to device adaptation can be controlled using There are various React libraries wrapping JavaScript Media Query API's, e.g.,
JavaScript Media Query API's. There are several React libraries that provide a
means to do this, e.g.,
[react-media-queries](https://github.com/bloodyowl/react-media-queries), [react-media-queries](https://github.com/bloodyowl/react-media-queries),
[media-query-fascade](https://github.com/tanem/media-query-facade), or [media-query-fascade](https://github.com/tanem/media-query-facade), or
[react-responsive](https://github.com/contra/react-responsive). This approach [react-responsive](https://github.com/contra/react-responsive). This has the
has the benefit of co-locating breakpoint-specific DOM and style changes. benefit of co-locating breakpoint-specific DOM and style changes.
## Pseudo-classes and pseudo-elements ## Pseudo-classes and pseudo-elements
Pseudo-classes like `:hover` and `:focus` can be implemented with the `onHover` Pseudo-classes like `:hover` and `:focus` can be implemented with the events
and `onFocus` events. Pseudo-elements are not supported; elements should be (e.g. `onFocus`). Pseudo-elements are not supported; elements should be used
used instead. instead.
## How it works ## How it works
@@ -149,7 +154,7 @@ corresponding `className`'s.
By doing this, the total size of the generated CSS is determined by the By doing this, the total size of the generated CSS is determined by the
total number of unique declarations (rather than the total number of rules in total number of unique declarations (rather than the total number of rules in
the application), making it viable to inline the style sheet when pre-rendering the application), making it viable to inline the style sheet when pre-rendering
on the server. on the server. Styles are updated if new module bundle are loaded asynchronously.
JavaScript definition: JavaScript definition:
+7 -5
View File
@@ -1,7 +1,7 @@
import GridView from './GridView' import GridView from './GridView'
import Heading from './Heading' import Heading from './Heading'
import MediaQueryWidget from './MediaQueryWidget' import MediaQueryWidget from './MediaQueryWidget'
import React, { Image, StyleSheet, ScrollView, Text, TextInput, Touchable, View } from '../../src' import React, { Image, StyleSheet, ScrollView, Text, TextInput, TouchableHighlight, View } from 'react-native'
export default class App extends React.Component { export default class App extends React.Component {
static propTypes = { static propTypes = {
@@ -25,7 +25,8 @@ export default class App extends React.Component {
} }
return ( return (
<View accessibilityRole='main' style={rootStyles}> <ScrollView accessibilityRole='main'>
<View style={rootStyles}>
<Heading size='xlarge'>React Native for Web</Heading> <Heading size='xlarge'>React Native for Web</Heading>
<Text>React Native Web takes the core components from <Text <Text>React Native Web takes the core components from <Text
accessibilityRole='link' href='https://facebook.github.io/react-native/'>React accessibilityRole='link' href='https://facebook.github.io/react-native/'>React
@@ -107,7 +108,7 @@ export default class App extends React.Component {
/> />
<Heading size='large'>Touchable</Heading> <Heading size='large'>Touchable</Heading>
<Touchable <TouchableHighlight
accessibilityLabel={'Touchable element'} accessibilityLabel={'Touchable element'}
activeHighlight='lightblue' activeHighlight='lightblue'
activeOpacity={0.8} activeOpacity={0.8}
@@ -119,7 +120,7 @@ export default class App extends React.Component {
<View style={styles.touchableArea}> <View style={styles.touchableArea}>
<Text>Touchable area (press, long press)</Text> <Text>Touchable area (press, long press)</Text>
</View> </View>
</Touchable> </TouchableHighlight>
<Heading size='large'>View</Heading> <Heading size='large'>View</Heading>
<Heading>Default layout</Heading> <Heading>Default layout</Heading>
@@ -205,7 +206,8 @@ export default class App extends React.Component {
))} ))}
</ScrollView> </ScrollView>
</View> </View>
</View> </View>
</ScrollView>
) )
} }
} }
+9 -5
View File
@@ -2,7 +2,7 @@ import { MediaProvider, matchMedia } from 'react-media-queries'
import App from './components/App' import App from './components/App'
import createGetter from 'react-media-queries/lib/createMediaQueryGetter' import createGetter from 'react-media-queries/lib/createMediaQueryGetter'
import createListener from 'react-media-queries/lib/createMediaQueryListener' import createListener from 'react-media-queries/lib/createMediaQueryListener'
import React from '../src' import React, { AppRegistry } from '../src'
const mediaQueries = { const mediaQueries = {
small: '(min-width: 300px)', small: '(min-width: 300px)',
@@ -10,10 +10,14 @@ const mediaQueries = {
large: '(min-width: 500px)' large: '(min-width: 500px)'
} }
const ResponsiveApp = matchMedia()(App) const ResponsiveApp = matchMedia()(App)
const WrappedApp = () => (
React.render(
<MediaProvider getMedia={createGetter(mediaQueries)} listener={createListener(mediaQueries)}> <MediaProvider getMedia={createGetter(mediaQueries)} listener={createListener(mediaQueries)}>
<ResponsiveApp /> <ResponsiveApp />
</MediaProvider>, </MediaProvider>
document.getElementById('react-root')
) )
AppRegistry.registerComponent('Example', () => WrappedApp)
AppRegistry.runApplication('Example', {
rootTag: document.getElementById('react-root')
})