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

197
README.md
View File

@@ -2,114 +2,56 @@
[![Build Status][travis-image]][travis-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
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)
[React Native][react-native-url] components and APIs for the Web.
## Quick start
You can [try the latest version on CodePen](http://codepen.io/necolas/pen/PZzwBR).
To install in your app:
```
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
### 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
`react-native-web` module:
```js
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`.
For example, the [`View`](docs/apis/View.md) component makes it easy to build
common layouts with flexbox, such as stacked and nested boxes with margin and
padding. And the [`StyleSheet`](docs/guides/style.md) API converts styles
defined in JavaScript to "atomic" CSS.
## Example
More examples can be found in the [`examples` directory](examples).
```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 Title = ({ children }) => <Text style={styles.title}>{children}</Text>
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({
card: {
flexGrow: 1,
@@ -121,73 +63,52 @@ const styles = StyleSheet.create({
},
image: {
height: 40,
marginRight: 10,
marginVertical: 10,
width: 40
}
})
```
## APIs
## Documentation
### [`StyleSheet`](docs/apis/StyleSheet.md)
Guides:
StyleSheet is a style abstraction that transforms inline styles to CSS on the
client or the server. It provides a minimal CSS reset targeting elements and
pseudo-elements beyond the reach of React inline styles.
* [Accessibility](docs/guides/accessibility.md)
* [Client and server rendering](docs/guides/rendering.md)
* [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)
An accessibile image component with support for image resizing, default image,
and child content.
### [`ListView`](docs/components/ListView.md)
(TODO)
### [`ScrollView`](docs/components/ScrollView.md)
A scrollable view with event throttling.
### [`Text`](docs/components/Text.md)
Displays text inline and supports basic press handling.
### [`TextInput`](docs/components/TextInput.md)
Accessible single- and multi-line text input via a keyboard.
### [`Touchable`](docs/components/Touchable.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`.
* Components
* [`ActivityIndicator`](docs/components/ActivityIndicator.md)
* [`Image`](docs/components/Image.md)
* [`ListView`](docs/components/ListView.md)
* [`Portal`](docs/components/Portal.md)
* [`ScrollView`](docs/components/ScrollView.md)
* [`Text`](docs/components/Text.md)
* [`TextInput`](docs/components/TextInput.md)
* [`TouchableHighlight`](docs/components/TouchableHighlight.md)
* [`TouchableOpacity`](docs/components/TouchableOpacity.md)
* [`TouchableWithoutFeedback`](docs/components/TouchableWithoutFeedback.md)
* [`View`](docs/components/View.md)
* APIs
* [`AppRegistry`](docs/apis/AppRegistry.md)
* [`AppState`](docs/apis/AppState.md)
* [`AsyncStorage`](docs/apis/AsyncStorage.md)
* [`Dimensions`](docs/apis/Dimensions.md)
* [`NativeMethods`](docs/apis/NativeMethods.md)
* [`NetInfo`](docs/apis/NetInfo.md)
* [`PixelRatio`](docs/apis/PixelRatio.md)
* [`Platform`](docs/apis/Platform.md)
* [`StyleSheet`](docs/apis/StyleSheet.md)
## License
Copyright (c) 2015 Nicolas Gallagher. Released under the [MIT
license](http://www.opensource.org/licenses/mit-license.php).
React Native for Web is [BSD licensed](LICENSE).
[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-url]: https://npmjs.org/package/react-native-web
[react-native-url]: https://facebook.github.io/react-native/

View File

@@ -27,5 +27,10 @@ module.exports = {
}),
new webpack.optimize.DedupePlugin(),
new webpack.optimize.OccurenceOrderPlugin()
]
],
resolve: {
alias: {
'react-native': '../../src'
}
}
}

View File

@@ -27,10 +27,10 @@ Remove a handler by passing the change event `type` and the `handler`.
## Examples
To see the current state, you can check `AppStateIOS.currentState`, which will
be kept up-to-date. This example will only ever appear to say "Current state
is: active" because the app is only visible to the user when in the `active`
state, and the null state will happen only momentarily.
To see the current state, you can check `AppState.currentState`, which will be
kept up-to-date. This example will only ever appear to say "Current state is:
active" because the app is only visible to the user when in the `active` state,
and the null state will happen only momentarily.
```js
class Example extends React.Component {

View File

@@ -9,12 +9,13 @@ The most common and best supported accessibility features of the Web are
exposed as the props: `accessible`, `accessibilityLabel`,
`accessibilityLiveRegion`, and `accessibilityRole`.
React Native for Web does not provide a way to directly control the rendered
HTML element. The `accessibilityRole` prop is used to infer an [analogous HTML
element][html-aria-url] to use in addition, where possible. While this may
contradict some ARIA recommendations, it also helps avoid certain HTML5
conformance errors and accessibility anti-patterns (e.g., giving a `heading`
role to a `button` element).
React Native for Web does not provide a way to directly control the type of the
rendered HTML element. The `accessibilityRole` prop is used to infer an
[analogous HTML element][html-aria-url] to use in addition to the resulting
ARIA `role`, where possible. While this may contradict some ARIA
recommendations, it also helps avoid certain HTML5 conformance errors and
accessibility anti-patterns (e.g., giving a `heading` role to a `button`
element).
For example:
@@ -24,7 +25,8 @@ For example:
* `<Text accessibilityRole='link' href='/' />` => `<a role='link' href='/' />`.
* `<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/
[html-accessibility-url]: http://www.html5accessibility.com/

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>
)
}
}
```

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)

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
docs/guides/rendering.md Normal file
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} />)
```

View File

@@ -42,7 +42,7 @@ documentation of individual components.
## Using styles
All the core components accept a `style` attribute.
All the React Native components accept a `style` attribute.
```js
<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:
```js
// either
<View style={{
...styles.base,
...(this.state.active && styles.active)
}} />
// or
<View style={[
styles.base,
this.state.active && styles.active
]} />
```
## Composing styles
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
order to make sure only styles are being passed.
order to make sure only valid styles are being passed.
```js
export default class List extends React.Component {
class List extends React.Component {
static propTypes = {
style: View.propTypes.style,
elementStyle: View.propTypes.style,
@@ -108,11 +115,11 @@ class List extends React.Component {
return (
<View
children={children}
style={{
...this.props.style,
style={[
this.props.style,
// 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
elements.
Changing styles in response to device adaptation can be controlled using
JavaScript Media Query API's. There are several React libraries that provide a
means to do this, e.g.,
There are various React libraries wrapping JavaScript Media Query API's, e.g.,
[react-media-queries](https://github.com/bloodyowl/react-media-queries),
[media-query-fascade](https://github.com/tanem/media-query-facade), or
[react-responsive](https://github.com/contra/react-responsive). This approach
has the benefit of co-locating breakpoint-specific DOM and style changes.
[react-responsive](https://github.com/contra/react-responsive). This has the
benefit of co-locating breakpoint-specific DOM and style changes.
## Pseudo-classes and pseudo-elements
Pseudo-classes like `:hover` and `:focus` can be implemented with the `onHover`
and `onFocus` events. Pseudo-elements are not supported; elements should be
used instead.
Pseudo-classes like `:hover` and `:focus` can be implemented with the events
(e.g. `onFocus`). Pseudo-elements are not supported; elements should be used
instead.
## How it works
@@ -149,7 +154,7 @@ corresponding `className`'s.
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
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:

View File

@@ -1,7 +1,7 @@
import GridView from './GridView'
import Heading from './Heading'
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 {
static propTypes = {
@@ -25,7 +25,8 @@ export default class App extends React.Component {
}
return (
<View accessibilityRole='main' style={rootStyles}>
<ScrollView accessibilityRole='main'>
<View style={rootStyles}>
<Heading size='xlarge'>React Native for Web</Heading>
<Text>React Native Web takes the core components from <Text
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>
<Touchable
<TouchableHighlight
accessibilityLabel={'Touchable element'}
activeHighlight='lightblue'
activeOpacity={0.8}
@@ -119,7 +120,7 @@ export default class App extends React.Component {
<View style={styles.touchableArea}>
<Text>Touchable area (press, long press)</Text>
</View>
</Touchable>
</TouchableHighlight>
<Heading size='large'>View</Heading>
<Heading>Default layout</Heading>
@@ -205,7 +206,8 @@ export default class App extends React.Component {
))}
</ScrollView>
</View>
</View>
</View>
</ScrollView>
)
}
}

View File

@@ -2,7 +2,7 @@ import { MediaProvider, matchMedia } from 'react-media-queries'
import App from './components/App'
import createGetter from 'react-media-queries/lib/createMediaQueryGetter'
import createListener from 'react-media-queries/lib/createMediaQueryListener'
import React from '../src'
import React, { AppRegistry } from '../src'
const mediaQueries = {
small: '(min-width: 300px)',
@@ -10,10 +10,14 @@ const mediaQueries = {
large: '(min-width: 500px)'
}
const ResponsiveApp = matchMedia()(App)
React.render(
const WrappedApp = () => (
<MediaProvider getMedia={createGetter(mediaQueries)} listener={createListener(mediaQueries)}>
<ResponsiveApp />
</MediaProvider>,
document.getElementById('react-root')
</MediaProvider>
)
AppRegistry.registerComponent('Example', () => WrappedApp)
AppRegistry.runApplication('Example', {
rootTag: document.getElementById('react-root')
})