mirror of
https://github.com/zoriya/react-native-web.git
synced 2026-05-28 08:34:31 +00:00
Monorepo
Introduces a monorepo structure, relies on yarn workspaces to share
dependencies, and lerna for syncing versions across the monorepo.
* Create 2 workspaces:
'packages' and 'website'
* Create 2 public packages:
'babel-plugin-react-native-web' and 'react-native-web'
* Create 1 private package:
'benchmarks'
A simple release script runs the tests, builds the package assets,
increments the package version numbers, git commits and tags, publishes
the package to npm, pushes the changes to github, and releases the
website update.
Close #657
This commit is contained in:
@@ -1,125 +0,0 @@
|
||||
# Accessibility
|
||||
|
||||
On the Web, assistive technologies (e.g., VoiceOver, TalkBack screen readers)
|
||||
derive useful information about the structure, purpose, and interactivity of
|
||||
apps from their [HTML elements][html-accessibility-url], attributes, and [ARIA
|
||||
in HTML][aria-in-html-url]. React Native for Web includes APIs designed to
|
||||
provide developers with support for making apps more accessible. The most
|
||||
common and best supported accessibility features of the Web are exposed as the
|
||||
props: `accessible`, `accessibilityLabel`, `accessibilityLiveRegion`,
|
||||
`accessibilityRole`, and `importantForAccessibility`.
|
||||
|
||||
## Accessibility properties
|
||||
|
||||
### accessible
|
||||
|
||||
When `true`, indicates that the view is an accessibility element. When a view
|
||||
is an accessibility element, it groups its children into a single focusable
|
||||
component. By default, all touchable elements, buttons, and links are
|
||||
"accessible". Prefer using `accessibilityRole` (e.g., `button`, `link`) to
|
||||
create focusable HTML elements wherever possible. On web, `accessible={true}`
|
||||
is implemented using `tabIndex`.
|
||||
|
||||
### accessibilityLabel
|
||||
|
||||
When a view is marked as `accessible`, it is a good practice to set an
|
||||
`accessibilityLabel` on the view, so that people who use screen readers know
|
||||
what element they have selected. On web, `accessibilityLabel` is implemented
|
||||
using `aria-label`.
|
||||
|
||||
```
|
||||
<TouchableOpacity accessibilityLabel={'Tap me!'} accessible={true} onPress={this._onPress}>
|
||||
<View style={styles.button}>
|
||||
<Text style={styles.buttonText}>Press me!</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
```
|
||||
|
||||
### accessibilityRole
|
||||
|
||||
In some cases, we also want to alert the end user of the type of selected
|
||||
component (i.e., that it is a “button”). To provide more context to screen
|
||||
readers, you should specify the `accessibilityRole` property. (Note that React
|
||||
Native for Web also provides a compatibility mapping of equivalent
|
||||
`accessibilityTraits` and `accessibilityComponentType` values to
|
||||
`accessibilityRole`).
|
||||
|
||||
The `accessibilityRole` prop is used to infer an [analogous HTML
|
||||
element][html-aria-url] and ARIA `role`, where possible. In most cases, both
|
||||
the element and ARIA `role` are rendered. 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) and browser bugs.
|
||||
|
||||
For example:
|
||||
|
||||
* `<View accessibilityRole='article' />` => `<article role='article' />`.
|
||||
* `<View accessibilityRole='banner' />` => `<header role='banner' />`.
|
||||
* `<View accessibilityRole='button' />` => `<div role='button' tabIndex='0' />`.
|
||||
* `<Text accessibilityRole='label' />` => `<label />`.
|
||||
* `<Text accessibilityRole='link' href='/' />` => `<a role='link' href='/' />`.
|
||||
* `<View accessibilityRole='main' />` => `<main role='main' />`.
|
||||
|
||||
In the example below, the `TouchableHighlight` is announced by screen
|
||||
readers as a button.
|
||||
|
||||
```js
|
||||
<TouchableHighlight accessibilityRole="button" onPress={this._handlePress}>
|
||||
<View style={styles.button}>
|
||||
<Text style={styles.buttonText}>Press me!</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
```
|
||||
|
||||
Note: The `button` role is not implemented using the native `button` element
|
||||
due to browsers limiting the use of flexbox layout on its children.
|
||||
|
||||
Note: Avoid changing `accessibilityRole` values over time or after user
|
||||
actions. Generally, accessibility APIs do not provide a means of notifying
|
||||
assistive technologies of a `role` value change.
|
||||
|
||||
### accessibilityLiveRegion
|
||||
|
||||
When components dynamically change we may need to inform the user. The
|
||||
`accessibilityLiveRegion` property serves this purpose and can be set to
|
||||
`none`, `polite` and `assertive`. On web, `accessibilityLiveRegion` is
|
||||
implemented using `aria-live`.
|
||||
|
||||
* `none`: Accessibility services should not announce changes to this view.
|
||||
* `polite`: Accessibility services should announce changes to this view.
|
||||
* `assertive`: Accessibility services should interrupt ongoing speech to immediately announce changes to this view.
|
||||
|
||||
```
|
||||
<TouchableWithoutFeedback onPress={this._addOne}>
|
||||
<View style={styles.embedded}>
|
||||
<Text>Click me</Text>
|
||||
</View>
|
||||
</TouchableWithoutFeedback>
|
||||
|
||||
<Text accessibilityLiveRegion="polite">
|
||||
Clicked {this.state.count} times
|
||||
</Text>
|
||||
```
|
||||
|
||||
In the above example, method `_addOne` changes the `state.count` variable. As
|
||||
soon as an end user clicks the `TouchableWithoutFeedback`, screen readers
|
||||
announce text in the `Text` view because of its
|
||||
`accessibilityLiveRegion="polite"` property.
|
||||
|
||||
### importantForAccessibility
|
||||
|
||||
The `importantForAccessibility` property controls if a view appears in the
|
||||
accessibility tree and if it is reported to accessibility services. On web, a
|
||||
value of `no` will remove a focusable element from the tab flow, and a value of
|
||||
`no-hide-descendants` will also hide the entire subtree from assistive
|
||||
technologies (this is implemented using `aria-hidden`).
|
||||
|
||||
### Other
|
||||
|
||||
Other ARIA properties can be set via [direct
|
||||
manipulation](./direct-manipulation.md) or props (this may change in the
|
||||
future).
|
||||
|
||||
[aria-in-html-url]: https://w3c.github.io/aria-in-html/
|
||||
[html-accessibility-url]: http://www.html5accessibility.com/
|
||||
[html-aria-url]: http://www.w3.org/TR/html-aria/
|
||||
@@ -1,113 +0,0 @@
|
||||
# Advanced use
|
||||
|
||||
## Use with existing React DOM components
|
||||
|
||||
React Native for Web exports a web-specific module called `createElement`,
|
||||
which can be used to wrap React DOM components. This allows you to use React
|
||||
Native's accessibility and style optimizations.
|
||||
|
||||
In the example below, `Video` will now accept common React Native props such as
|
||||
`accessibilityLabel`, `accessible`, `style`, and even the Responder event
|
||||
props.
|
||||
|
||||
```js
|
||||
import { createElement } from 'react-native-web';
|
||||
const Video = (props) => createElement('video', props);
|
||||
```
|
||||
|
||||
This also works with composite components defined in your existing component
|
||||
gallery or dependencies ([live example](https://www.webpackbin.com/bins/-KiTSGFw3fB9Szg7quLI)).
|
||||
|
||||
```js
|
||||
import RaisedButton from 'material-ui/RaisedButton';
|
||||
import { createElement } from 'react-native-web';
|
||||
import { StyleSheet } from 'react-native';
|
||||
|
||||
const CustomButton = (props) => createElement(RaisedButton, {
|
||||
...props,
|
||||
style: [ styles.button, props.style ]
|
||||
});
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
button: {
|
||||
padding: 20
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
And `createElement` can be used as drop-in replacement for `React.createElement`:
|
||||
|
||||
```js
|
||||
/* @jsx createElement */
|
||||
import { createElement } from 'react-native-web';
|
||||
const Video = (props) => <video {...props} style={[ { marginVertical: 10 }, props.style ]} />
|
||||
```
|
||||
|
||||
Remember that React Native styles are not the same as React DOM styles, and
|
||||
care needs to be taken not to pass React DOM styles into your React Native
|
||||
wrapped components.
|
||||
|
||||
## Use as a library framework
|
||||
|
||||
The React Native (for Web) building blocks can be used to create higher-level
|
||||
components and abstractions. In the example below, a `styled` function provides
|
||||
an API inspired by styled-components ([live
|
||||
example](https://www.webpackbin.com/bins/-KjT9ziwv4O7FDZdvsnX)).
|
||||
|
||||
```js
|
||||
import { createElement } from 'react-native-web';
|
||||
import { StyleSheet } from 'react-native';
|
||||
|
||||
/**
|
||||
* styled API
|
||||
*/
|
||||
const styled = (Component, styler) => {
|
||||
const isDOMComponent = typeof Component === 'string';
|
||||
|
||||
class Styled extends React.Component {
|
||||
static contextTypes = {
|
||||
getTheme: React.PropTypes.func
|
||||
};
|
||||
|
||||
render() {
|
||||
const theme = this.context.getTheme && this.context.getTheme();
|
||||
const localProps = { ...this.props, theme };
|
||||
const nextProps = { ...this.props }
|
||||
const style = typeof styler === 'function' ? styler(localProps) : styler;
|
||||
nextProps.style = [ style, this.props.style ];
|
||||
|
||||
return (
|
||||
isDOMComponent
|
||||
? createElement(Component, nextProps)
|
||||
: <Component {...nextProps} />
|
||||
);
|
||||
}
|
||||
}
|
||||
return Styled;
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
alignItems: 'center',
|
||||
backgroundColor: '#2196F3',
|
||||
flex: 1,
|
||||
justifyContent: 'center'
|
||||
}
|
||||
});
|
||||
|
||||
const StyledView = styled(View, styles.container);
|
||||
```
|
||||
|
||||
## Use with react-sketchapp
|
||||
|
||||
Use with [react-sketchapp](http://airbnb.io/react-sketchapp/) requires that you
|
||||
alias `react-native` to `react-sketchapp`. This will allow you to render your
|
||||
existing React Native components in Sketch. Sketch-specific components like
|
||||
`Artboard` should be imported from `react-sketchapp`.
|
||||
|
||||
If you're using `skpm`, you can rely on an [undocumented
|
||||
feature](https://github.com/sketch-pm/skpm/blob/master/lib/utils/webpackConfig.js)
|
||||
which will merge your `webpack.config.js`, `.babelrc`, or `package.json` Babel
|
||||
config into its internal webpack config. The simplest option may be to use the
|
||||
[babel-plugin-module-alias](https://www.npmjs.com/package/babel-plugin-module-alias)
|
||||
and configure it in your `package.json`.
|
||||
@@ -1,162 +0,0 @@
|
||||
# Direct manipulation
|
||||
|
||||
React Native provides several methods to directly access the underlying host
|
||||
node. This can be useful when you need to make changes directly to a component
|
||||
without using state/props to trigger a re-render of the entire subtree, or when
|
||||
you want to focus a view or measure its on-screen dimensions.
|
||||
|
||||
The methods described are available on most of the default components provided
|
||||
by React Native for Web. Note, however, that they are *not* available on the composite
|
||||
components that you define in your own app.
|
||||
|
||||
## Instance methods
|
||||
|
||||
**blur**()
|
||||
|
||||
Removes focus from an input or view. This is the opposite of `focus()`.
|
||||
|
||||
**focus**()
|
||||
|
||||
Requests focus for the given input or view. The exact behavior triggered will
|
||||
depend the type of view.
|
||||
|
||||
**measure**(callback: (x, y, width, height, pageX, pageY) => void)
|
||||
|
||||
For a given view, `measure` determines the offset relative to the parent view,
|
||||
width, height, and the offset relative to the viewport. Returns the values via
|
||||
an async callback.
|
||||
|
||||
Note that these measurements are not available until after the rendering has
|
||||
been completed.
|
||||
|
||||
**measureLayout**(relativeToNativeNode: DOMNode, onSuccess: (x, y, width, height) => void)
|
||||
|
||||
Like `measure`, but measures the view relative to another view, specified as
|
||||
`relativeToNativeNode`. This means that the returned `x`, `y` are relative to
|
||||
the origin `x`, `y` of the ancestor view.
|
||||
|
||||
As always, to obtain a native node handle for a component, you can use
|
||||
`findNodeHandle(component)`.
|
||||
|
||||
**measureInWindow**(callback: (x, y, width, height) => void)
|
||||
|
||||
Determines the location of the given view in the window and returns the values
|
||||
via an async callback.
|
||||
|
||||
**setNativeProps**(nativeProps: Object)
|
||||
|
||||
This function sends props straight to the underlying DOM node.
|
||||
|
||||
## About `setNativeProps`
|
||||
|
||||
`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>
|
||||
)
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -1,240 +0,0 @@
|
||||
# Getting started
|
||||
|
||||
This guide will help you to correctly configure build and test tools to work
|
||||
with React Native for Web. (Alternatively, you can quickly setup a local
|
||||
project using the starter kits listed in the README.)
|
||||
|
||||
It is recommended that your application provide a `Promise` and `Array.from`
|
||||
polyfill.
|
||||
|
||||
## Web packager
|
||||
|
||||
[Webpack](https://webpack.js.org) is a popular build tool for web apps. Below is an
|
||||
_example_ of how to configure a build that uses [Babel](https://babeljs.io/) to
|
||||
compile your JavaScript for the web. Please refer to the webpack documentation
|
||||
when setting up your project.
|
||||
|
||||
Install webpack-related dependencies, for example:
|
||||
|
||||
```
|
||||
yarn add --dev babel-loader url-loader webpack webpack-dev-server
|
||||
```
|
||||
|
||||
Create a `web/webpack.config.js` file:
|
||||
|
||||
```js
|
||||
// web/webpack.config.js
|
||||
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
|
||||
const appDirectory = path.resolve(__dirname, '../');
|
||||
|
||||
// This is needed for webpack to compile JavaScript.
|
||||
// Many OSS React Native packages are not compiled to ES5 before being
|
||||
// published. If you depend on uncompiled packages they may cause webpack build
|
||||
// errors. To fix this webpack can be configured to compile to the necessary
|
||||
// `node_module`.
|
||||
const babelLoaderConfiguration = {
|
||||
test: /\.js$/,
|
||||
// Add every directory that needs to be compiled by Babel during the build.
|
||||
include: [
|
||||
path.resolve(appDirectory, 'index.web.js'),
|
||||
path.resolve(appDirectory, 'src'),
|
||||
path.resolve(appDirectory, 'node_modules/react-native-uncompiled')
|
||||
],
|
||||
use: {
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
cacheDirectory: true,
|
||||
// This aliases 'react-native' to 'react-native-web' and includes only
|
||||
// the modules needed by the app.
|
||||
plugins: ['react-native-web/babel', 'transform-runtime'],
|
||||
// The 'react-native' preset is recommended (or use your own .babelrc).
|
||||
presets: ['react-native']
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// This is needed for webpack to import static images in JavaScript files.
|
||||
const imageLoaderConfiguration = {
|
||||
test: /\.(gif|jpe?g|png|svg)$/,
|
||||
use: {
|
||||
loader: 'url-loader',
|
||||
options: {
|
||||
name: '[name].[ext]'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
// your web-specific entry file
|
||||
entry: path.resolve(appDirectory, 'index.web.js'),
|
||||
|
||||
// configures where the build ends up
|
||||
output: {
|
||||
filename: 'bundle.web.js',
|
||||
path: path.resolve(appDirectory, 'dist')
|
||||
},
|
||||
|
||||
// ...the rest of your config
|
||||
|
||||
module: {
|
||||
rules: [
|
||||
babelLoaderConfiguration,
|
||||
imageLoaderConfiguration
|
||||
]
|
||||
},
|
||||
|
||||
plugins: [
|
||||
// `process.env.NODE_ENV === 'production'` must be `true` for production
|
||||
// builds to eliminate development checks and reduce build size. You may
|
||||
// wish to include additional optimizations.
|
||||
new webpack.DefinePlugin({
|
||||
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'),
|
||||
__DEV__: process.env.NODE_ENV === 'production' || true
|
||||
})
|
||||
],
|
||||
|
||||
resolve: {
|
||||
// If you're working on a multi-platform React Native app, web-specific
|
||||
// module implementations should be written in files using the extension
|
||||
// `.web.js`.
|
||||
extensions: [ '.web.js', '.js' ]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To run in development from the root of your application:
|
||||
|
||||
```
|
||||
./node_modules/.bin/webpack-dev-server -d --config ./web/webpack.config.js --inline --hot --colors
|
||||
```
|
||||
|
||||
To build for production:
|
||||
|
||||
```
|
||||
./node_modules/.bin/webpack -p --config ./web/webpack.config.js
|
||||
```
|
||||
|
||||
Please refer to the Webpack documentation for more information on configuration.
|
||||
|
||||
### Client-side rendering
|
||||
|
||||
Rendering using `AppRegistry`:
|
||||
|
||||
```js
|
||||
// index.web.js
|
||||
|
||||
import App from './src/App';
|
||||
import React from 'react';
|
||||
import ReactNative, { AppRegistry } from 'react-native';
|
||||
|
||||
// register the app
|
||||
AppRegistry.registerComponent('App', () => App);
|
||||
|
||||
AppRegistry.runApplication('App', {
|
||||
initialProps: {},
|
||||
rootTag: document.getElementById('react-app')
|
||||
});
|
||||
```
|
||||
|
||||
Rendering within existing web apps is also possible using `ReactNative`:
|
||||
|
||||
```js
|
||||
import AppHeader from './src/AppHeader';
|
||||
import React from 'react';
|
||||
import ReactNative from 'react-native';
|
||||
|
||||
// use .hydrate if hydrating a SSR app
|
||||
ReactNative.render(<AppHeader />, document.getElementById('react-app-header'))
|
||||
```
|
||||
|
||||
And finally, `react-native-web` components will also be rendering within a tree
|
||||
produced by calling `ReactDOM.render` (i.e., an existing web app), but
|
||||
otherwise it is not recommended.
|
||||
|
||||
### Server-side rendering
|
||||
|
||||
Server-side rendering is supported using the `AppRegistry`:
|
||||
|
||||
```js
|
||||
import App from './src/App';
|
||||
import ReactDOMServer from 'react-dom/server'
|
||||
import ReactNative, { AppRegistry } from 'react-native'
|
||||
|
||||
// register the app
|
||||
AppRegistry.registerComponent('App', () => App)
|
||||
|
||||
// prerender the app
|
||||
const { element, stylesheets } = AppRegistry.getApplication('App', { initialProps });
|
||||
const initialHTML = ReactDOMServer.renderToString(element);
|
||||
const initialStyles = stylesheets.map((sheet) => ReactDOMServer.renderToStaticMarkup(sheet)).join('\n');
|
||||
|
||||
// construct HTML document
|
||||
const document = `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
${initialStyles}
|
||||
</head>
|
||||
<body>
|
||||
${initialHTML}
|
||||
`
|
||||
```
|
||||
|
||||
## Web-specific code
|
||||
|
||||
Minor platform differences can use the `Platform` module.
|
||||
|
||||
```js
|
||||
import { Platform } from 'react-native';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
height: (Platform.OS === 'web') ? 200 : 100,
|
||||
});
|
||||
```
|
||||
|
||||
More significant platform differences should use platform-specific files (see
|
||||
the webpack configuration above for resolving `*.web.js` files):
|
||||
|
||||
For example, with the following files in your project:
|
||||
|
||||
```
|
||||
MyComponent.android.js
|
||||
MyComponent.ios.js
|
||||
MyComponent.web.js
|
||||
```
|
||||
|
||||
And the following import:
|
||||
|
||||
```js
|
||||
import MyComponent from './MyComponent';
|
||||
```
|
||||
|
||||
React Native will automatically import the correct variant for each specific
|
||||
target platform.
|
||||
|
||||
## Testing with Jest
|
||||
|
||||
[Jest](https://facebook.github.io/jest/) can be configured to improve snapshots
|
||||
of `react-native-web` components.
|
||||
|
||||
```
|
||||
{
|
||||
"snapshotSerializers": [ "enzyme-to-json/serializer", "react-native-web/jest/serializer" ]
|
||||
}
|
||||
```
|
||||
|
||||
Jest also needs to map `react-native` to `react-native-web` (unless you are
|
||||
using Babel with the `react-native-web/babel` plugin).
|
||||
|
||||
```
|
||||
{
|
||||
"moduleNameMapper": {
|
||||
"react-native": "<rootDir>/node_modules/react-native-web"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Please refer to the Jest documentation for more information.
|
||||
@@ -1,29 +0,0 @@
|
||||
# Internationalization
|
||||
|
||||
To support right-to-left languages, application layout can be automatically
|
||||
flipped from LTR to RTL. The `I18nManager` API can be used to help with more
|
||||
fine-grained control and testing of RTL layouts.
|
||||
|
||||
## Working with icons and images
|
||||
|
||||
Icons and images that must match the LTR or RTL layout of the app need to be manually flipped.
|
||||
|
||||
Either use a transform style:
|
||||
|
||||
```js
|
||||
<Image
|
||||
source={...}
|
||||
style={{ transform: [{ scaleX: I18nManager.isRTL ? -1 : 1 }] }}
|
||||
/>
|
||||
```
|
||||
|
||||
Or replace the source asset:
|
||||
|
||||
```js
|
||||
import imageSourceLTR from './back.png';
|
||||
import imageSourceRTL from './forward.png';
|
||||
|
||||
<Image
|
||||
source={I18nManager.isRTL ? imageSourceRTL : imageSourceLTR}
|
||||
/>
|
||||
```
|
||||
@@ -1,31 +0,0 @@
|
||||
# Known issues
|
||||
|
||||
## Safari flexbox performance
|
||||
|
||||
Safari version prior to 10.1 can suffer from extremely [poor flexbox
|
||||
performance](https://bugs.webkit.org/show_bug.cgi?id=150445). The recommended
|
||||
way to work around this issue (as used on mobile.twitter.com) is to set
|
||||
`display:block` on Views in your element hierarchy that you know don't need
|
||||
flexbox layout.
|
||||
|
||||
## Missing modules and components
|
||||
|
||||
Not all of the views present on iOS/Android are currently available 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 or to guard the use of a
|
||||
module with `Platform.OS` (e.g. `NativeModules`)
|
||||
|
||||
## Missing component props
|
||||
|
||||
There are properties that do not work across all platforms. All web-specific
|
||||
props are annotated with `(web)` in the documentation.
|
||||
|
||||
## 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` for Web use, in anticipation
|
||||
of the convergence between the iOS and Android components in React Native.
|
||||
@@ -1,232 +0,0 @@
|
||||
# Style
|
||||
|
||||
React Native relies on JavaScript to define and resolve the styles of your
|
||||
application. React Native for Web implements the React Native style API in a
|
||||
way that avoids *all* the [problems with CSS at
|
||||
scale](https://speakerdeck.com/vjeux/react-css-in-js):
|
||||
|
||||
1. No local variables
|
||||
2. Implicit dependencies
|
||||
3. No dead code elimination
|
||||
4. No code minification
|
||||
5. No sharing of constants
|
||||
6. Non-deterministic resolution
|
||||
7. No isolation
|
||||
|
||||
At the same time, it has several benefits:
|
||||
|
||||
1. Simple API and expressive subset of CSS
|
||||
2. Generates CSS; the minimum required
|
||||
3. Good runtime performance
|
||||
4. Support for static and dynamic styles
|
||||
5. Support for RTL layouts
|
||||
6. Easy pre-rendering of critical CSS
|
||||
|
||||
## Defining styles
|
||||
|
||||
Styles should be defined outside of the component. Using `StyleSheet.create` is
|
||||
optional but provides the best performance (by relying on generated CSS
|
||||
stylesheets). Avoid creating unregistered style objects.
|
||||
|
||||
```js
|
||||
const styles = StyleSheet.create({
|
||||
heading: {
|
||||
color: 'gray',
|
||||
fontSize: '2rem'
|
||||
},
|
||||
text: {
|
||||
marginTop: '1rem',
|
||||
margin: 10
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
See the `style` documentation of individual components for supported properties.
|
||||
|
||||
## Using styles
|
||||
|
||||
All the React Native components accept a `style` property. The value can be a
|
||||
registered object, a plain object, or an array.
|
||||
|
||||
```js
|
||||
// registered object
|
||||
<View style={styles.view} />
|
||||
|
||||
// plain object
|
||||
<View style={{ transform: [ { translateX } ] }} />
|
||||
|
||||
// array of registered or plain objects
|
||||
<View style={[ styles.container, this.props.style ]} />
|
||||
```
|
||||
|
||||
The array syntax will merge styles from left-to-right as normal JavaScript
|
||||
objects, and can be used to conditionally apply styles:
|
||||
|
||||
```js
|
||||
<View style={[
|
||||
styles.container,
|
||||
this.state.active && styles.active
|
||||
]} />
|
||||
```
|
||||
|
||||
When styles are registered with `StyleSheet.create`, the return value is a
|
||||
number and not a style object. This is important for performance optimizations,
|
||||
but still allows you to merge styles in a deterministic manner at runtime. If
|
||||
you need access to the underlying style objects you need to use
|
||||
`StyleSheet.flatten` (but be aware that this is not the optimized path).
|
||||
|
||||
## Composing styles
|
||||
|
||||
To let other components customize the style of a component's children you can
|
||||
expose a prop so styles can be explicitly passed into the component.
|
||||
|
||||
```js
|
||||
class List extends React.Component {
|
||||
static propTypes = {
|
||||
style: ViewPropTypes.style,
|
||||
elementStyle: ViewPropTypes.style,
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View style={this.props.style}>
|
||||
{elements.map((element) =>
|
||||
<View style={[ styles.element, this.props.elementStyle ]} />
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In another file:
|
||||
|
||||
```js
|
||||
<List style={styles.list} elementStyle={styles.listElement} />
|
||||
```
|
||||
|
||||
You also have much greater control over how styles are composed when compared
|
||||
to using class names. For example, you may choose to accept a limited subset
|
||||
of style props in the component's API, and control when they are applied.
|
||||
|
||||
## How styles are resolved
|
||||
|
||||
React Native style resolution is deterministic and slightly different from CSS.
|
||||
|
||||
In the following HTML/CSS example, the `.margin` selector is defined last in
|
||||
the CSS and takes precedence over the previous rules, resulting in a margin of
|
||||
`0, 0, 0, 0`.
|
||||
|
||||
```html
|
||||
<style>
|
||||
.marginTop { margin-top: 10px; }
|
||||
.marginBottom { margin-bottom: 20px; }
|
||||
.margin { margin: 0; }
|
||||
</style>
|
||||
<div class="marginTop marginBottom margin"></div>
|
||||
```
|
||||
|
||||
But in React Native the most *specific* style property takes precedence,
|
||||
resulting in margins of `10, 0, 20, 0`.
|
||||
|
||||
```js
|
||||
const style = [
|
||||
{ marginTop: 10 },
|
||||
{ marginBottom: 20 },
|
||||
{ margin: 0 }
|
||||
];
|
||||
|
||||
const Box = () => <View style={style} />
|
||||
```
|
||||
|
||||
## Implementation details
|
||||
|
||||
React Native for Web transforms React Native styles into React DOM styles. Any
|
||||
styles defined using `StyleSheet.create` will ultimately be rendered using CSS
|
||||
class names.
|
||||
|
||||
React Native for Web introduced a novel strategy to achieve this. Each rule is
|
||||
broken down into declarations, properties are expanded to their long-form, and
|
||||
the resulting key-value pairs are mapped to unique "atomic CSS" class names.
|
||||
|
||||
Input:
|
||||
|
||||
```js
|
||||
const Box = () => <View style={styles.box} />
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
box: {
|
||||
margin: 0
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```html
|
||||
<style>
|
||||
.rn-1mnahxq { margin-top: 0px; }
|
||||
.rn-61z16t { margin-right: 0px; }
|
||||
.rn-p1pxzi { margin-bottom: 0px; }
|
||||
.rn-11wrixw { margin-left: 0px; }
|
||||
</style>
|
||||
|
||||
<div class="rn-156q2ks rn-61z16t rn-p1pxzi rn-11wrixw"></div>
|
||||
```
|
||||
|
||||
This ensures that CSS order doesn't impact rendering and CSS rules are
|
||||
efficiently deduplicated. Rather than the total CSS growing in proportion to
|
||||
the number of *rules*, it grows in proportion to the number of *unique
|
||||
declarations*. As a result, the DOM style sheet is only written to when new
|
||||
unique declarations are defined and it is usually small enough to be
|
||||
pre-rendered and inlined.
|
||||
|
||||
Class names are deterministic, which means that the resulting CSS and HTML is
|
||||
consistent across builds – important for large apps using code-splitting and
|
||||
deploying incremental updates.
|
||||
|
||||
At runtime registered styles are resolved to DOM style props and memoized.
|
||||
Any dynamic styles that contain declarations previously registered as static
|
||||
styles can also be converted to CSS class names. Otherwise, they render as
|
||||
inline styles.
|
||||
|
||||
All this allows React Native for Web to support the rich functionality of React
|
||||
Native styles (including RTL layouts and `setNativeProps`) while providing one
|
||||
of the [fastest](https://github.com/necolas/react-native-web/blob/master/benchmarks/README.md),
|
||||
safest, and most efficient styles-in-JavaScript solutions.
|
||||
|
||||
## FAQs
|
||||
|
||||
### What about Media Queries?
|
||||
|
||||
`StyleSheet.create` is a way of defining the styles your application requires;
|
||||
it does not concern itself with _where_ or _when_ those styles are applied to
|
||||
elements.
|
||||
|
||||
Media Queries may not be most appropriate for component-based designs. React
|
||||
Native provides the `Dimensions` API and `onLayout` props. If you do need Media
|
||||
Queries, using the `matchMedia` DOM API has the benefit of allowing you to swap
|
||||
out entire components, not just styles. There are also many React libraries
|
||||
wrapping JavaScript Media Query API's, e.g.,
|
||||
[react-media](https://github.com/reacttraining/react-media),
|
||||
[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).
|
||||
|
||||
### What about pseudo-classes and pseudo-elements?
|
||||
|
||||
Pseudo-classes like `:hover` and `:focus` can be implemented with events (e.g.
|
||||
`onFocus`). Pseudo-elements are not supported; elements should be used instead.
|
||||
|
||||
### Do I need a CSS reset?
|
||||
|
||||
No. React Native for Web includes a very small CSS reset that removes unwanted
|
||||
User Agent styles from (pseudo-)elements beyond the reach of React (e.g.,
|
||||
`html`, `body`) or inline styles (e.g., `::-moz-focus-inner`). The rest is
|
||||
handled at the component-level.
|
||||
|
||||
### What about using Dev Tools?
|
||||
|
||||
React Dev Tools supports inspecting and editing of React Native styles. It's
|
||||
recommended that you rely more on React Dev Tools and live/hot-reloading rather
|
||||
than inspecting and editing the DOM directly.
|
||||
Reference in New Issue
Block a user