`. -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/ diff --git a/docs/guides/direct-manipulation.md b/docs/guides/direct-manipulation.md new file mode 100644 index 00000000..f6dad3e6 --- /dev/null +++ b/docs/guides/direct-manipulation.md @@ -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) => ( + + {props.label} + +) + +const App = () => ( + + + +) +``` + +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 ( + this._root = component}> + {this.props.label} + + ) + } +} +``` + +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 ( + + this._textInput = component} + style={styles.textInput} + /> + + Clear text + + + ) + } +} +``` diff --git a/docs/guides/known-issues.md b/docs/guides/known-issues.md new file mode 100644 index 00000000..944d382e --- /dev/null +++ b/docs/guides/known-issues.md @@ -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) diff --git a/docs/guides/react-native.md b/docs/guides/react-native.md new file mode 100644 index 00000000..f07517f0 --- /dev/null +++ b/docs/guides/react-native.md @@ -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') + }); +} +``` diff --git a/docs/guides/rendering.md b/docs/guides/rendering.md new file mode 100644 index 00000000..dbd1030d --- /dev/null +++ b/docs/guides/rendering.md @@ -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(
, document.getElementById('sidebar-app')) + +// Server render +React.renderToString(
) +``` + +## 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) => ( + + + + + {style} + + +
+ + +) + +React.renderToStaticMarkup() +``` diff --git a/docs/guides/style.md b/docs/guides/style.md index bae3273e..93c24822 100644 --- a/docs/guides/style.md +++ b/docs/guides/style.md @@ -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 @@ -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 + +// or + ``` ## 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 ( ) } @@ -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: diff --git a/examples/components/App.js b/examples/components/App.js index 87f71019..06f81a4c 100644 --- a/examples/components/App.js +++ b/examples/components/App.js @@ -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 ( - + + React Native for Web React Native Web takes the core components from React @@ -107,7 +108,7 @@ export default class App extends React.Component { /> Touchable - Touchable area (press, long press) - + View Default layout @@ -205,7 +206,8 @@ export default class App extends React.Component { ))} - + + ) } } diff --git a/examples/index.js b/examples/index.js index e155d0c5..7999f5c2 100644 --- a/examples/index.js +++ b/examples/index.js @@ -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 = () => ( - , - document.getElementById('react-root') + ) + +AppRegistry.registerComponent('Example', () => WrappedApp) + +AppRegistry.runApplication('Example', { + rootTag: document.getElementById('react-root') +})