mirror of
https://github.com/status-im/react-native.git
synced 2025-01-14 11:34:23 +00:00
Merge pull request #1656 from brentvatne/set-native-props-docs
[Docs] Add DirectManipluation guide to explain setNativeProps
This commit is contained in:
commit
0881b0f99f
242
docs/DirectManipulation.md
Normal file
242
docs/DirectManipulation.md
Normal file
@ -0,0 +1,242 @@
|
||||
---
|
||||
id: direct-manipulation
|
||||
title: Direct Manipulation
|
||||
layout: docs
|
||||
category: Guides
|
||||
permalink: docs/direct-manipulation.html
|
||||
next: linking-libraries
|
||||
---
|
||||
|
||||
It is sometimes necessary to make changes directly to a component
|
||||
without using state/props to trigger a re-render of the entire subtree.
|
||||
When using React in the browser for example, you sometimes need to
|
||||
directly modify a DOM node, and the same is true for views in mobile
|
||||
apps. `setNativeProps` is the React Native equivalent to setting
|
||||
properties directly on a DOM node.
|
||||
|
||||
> Use setNativeProps when frequent re-rendering creates a performance bottleneck
|
||||
>
|
||||
> Direct manipulation will not be a tool that you reach for
|
||||
> frequently; you will typically only be using it for creating
|
||||
> continuous animations to avoid the overhead of rendering the component
|
||||
> hierarchy and reconciling many views. `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 [shouldComponent](react-native/docs/direct-manipulation.html#setnativeprops-shouldcomponentupdate).
|
||||
|
||||
## setNativeProps with TouchableOpacity
|
||||
|
||||
[TouchableOpacity](https://github.com/facebook/react-native/blob/master/Libraries/Components/Touchable/TouchableOpacity.js)
|
||||
uses `setNativeProps` internally to update the opacity of its child
|
||||
component:
|
||||
|
||||
```javascript
|
||||
setOpacityTo: function(value) {
|
||||
// Redacted: animation related code
|
||||
this.refs[CHILD_REF].setNativeProps({
|
||||
opacity: value
|
||||
});
|
||||
},
|
||||
```
|
||||
|
||||
This allows us to write the following code and know that the child will
|
||||
have its opacity updated in response to taps, without the child having
|
||||
any knowledge of that fact or requiring any changes to its implementation:
|
||||
|
||||
```javascript
|
||||
<TouchableOpacity onPress={this._handlePress}>
|
||||
<View style={styles.button}>
|
||||
<Text>Press me!</Text>
|
||||
</View>
|
||||
<TouchableOpacity>
|
||||
```
|
||||
|
||||
Let's imagine that `setNativeProps` was not available. One way that we
|
||||
might implement it with that constraint is to store the opacity value
|
||||
in the state, then update that value whenever `onPress` is fired:
|
||||
|
||||
```javascript
|
||||
getInitialState() {
|
||||
return { myButtonOpacity: 1, }
|
||||
},
|
||||
|
||||
render() {
|
||||
return (
|
||||
<TouchableOpacity onPress={() => this.setState({myButtonOpacity: 0.5})}
|
||||
onPressOut={() => this.setState({myButtonOpacity: 1})}>
|
||||
<View style={[styles.button, {opacity: this.state.myButtonOpacity}]}>
|
||||
<Text>Press me!</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
This is computationally intensive compared to the original example -
|
||||
React needs to re-render the component hierarchy each time the opacity
|
||||
changes, even though other properties of the view and its children
|
||||
haven't changed. Usually this overhead isn't a concern but when
|
||||
performing continuous animations and responding to gestures, judiciously
|
||||
optimizing your components can improve your animations' fidelity.
|
||||
|
||||
If you look at the implementation of `setNativeProps` in
|
||||
[NativeMethodsMixin.js](https://github.com/facebook/react-native/blob/master/Libraries/ReactIOS/NativeMethodsMixin.js)
|
||||
you will notice that it is a wrapper around `RCTUIManager.updateView` -
|
||||
this is the exact same function call that results from re-rendering -
|
||||
see [receiveComponent in
|
||||
ReactNativeBaseComponent.js](https://github.com/facebook/react-native/blob/master/Libraries/ReactNative/ReactNativeBaseComponent.js).
|
||||
|
||||
## Composite components and setNativeProps
|
||||
|
||||
Composite components are not backed by a native view, so you cannot call
|
||||
`setNativeProps` on them. Consider this example:
|
||||
|
||||
```javascript
|
||||
var MyButton = React.createClass({
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<Text>{this.props.label}</Text>
|
||||
</View>
|
||||
)
|
||||
},
|
||||
});
|
||||
|
||||
var App = React.createClass({
|
||||
render() {
|
||||
return (
|
||||
<TouchableOpacity>
|
||||
<MyButton label="Press me!" />
|
||||
</TouchableOpacity>
|
||||
)
|
||||
},
|
||||
});
|
||||
```
|
||||
[Run this example](https://rnplay.org/apps/JXkgmQ)
|
||||
|
||||
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.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.
|
||||
|
||||
```javascript
|
||||
var MyButton = React.createClass({
|
||||
setNativeProps(nativeProps) {
|
||||
this._root.setNativeProps(nativeProps);
|
||||
},
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View ref={component => this._root = component} {...this.props}>
|
||||
<Text>{this.props.label}</Text>
|
||||
</View>
|
||||
)
|
||||
},
|
||||
});
|
||||
```
|
||||
[Run this example](https://rnplay.org/apps/YJxnEQ)
|
||||
|
||||
You can now use `MyButton` inside of `TouchableOpacity`! A sidenote for
|
||||
clarity: we used the [ref callback](https://facebook.github.io/react/docs/more-about-refs.html#the-ref-callback-attribute) syntax here, rather than the traditional string-based ref.
|
||||
|
||||
You may have noticed that we passed all of the props down to the child
|
||||
view using `{...this.props}`. The reason for this is that
|
||||
`TouchableOpacity` is actually a composite component, and so in addition
|
||||
to depending on `setNativeProps` on its child, it also requires that the
|
||||
child perform touch handling. To dot this, it passes on [various
|
||||
props](https://facebook.github.io/react-native/docs/view.html#onmoveshouldsetresponder)
|
||||
that call back to the `TouchableOpacity` component.
|
||||
`TouchableHighlight`, in contrast, is backed by a native view only
|
||||
requires that we implement `setNativeProps`.
|
||||
|
||||
## Precomputing style
|
||||
|
||||
We learned above that `setNativeProps` is a wrapper around
|
||||
`RCTUIManager.updateView`, which is also used internally by React to
|
||||
perform updates on re-render. One important difference is that
|
||||
`setNativeProps` does not call `precomputeStyle`, which is done
|
||||
internally by React, and so the `transform` property will not work if
|
||||
you try to update it manually with `setNativeProps`. To fix this,
|
||||
you can call `precomputeStyle` on your object first:
|
||||
|
||||
```javascript
|
||||
var precomputeStyle = require('precomputeStyle');
|
||||
|
||||
var App = React.createClass({
|
||||
componentDidMount() {
|
||||
var nativeProps = precomputeStyle({transform: [{rotate: '45deg'}]});
|
||||
this._root.setNativeProps(nativeProps);
|
||||
},
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View ref={component => this._root = component}
|
||||
style={styles.container}>
|
||||
<Text>Precompute style!</Text>
|
||||
</View>
|
||||
)
|
||||
},
|
||||
});
|
||||
```
|
||||
[Run this example](https://rnplay.org/apps/8_mIAA)
|
||||
|
||||
## setNativeProps to clear TextInput value
|
||||
|
||||
Another very common use case of `setNativeProps` is to clear the value
|
||||
of a TextInput. The `controlled` prop of TextInput can sometimes drop
|
||||
characters when the `bufferDelay` is low and the user types very
|
||||
quickly. Some developers prefer to skip this prop entirely and instead
|
||||
use `setNativeProps` to directly manipulate the TextInput value when
|
||||
necessary. For example, the following code demonstrates clearing the
|
||||
input when you tap a button:
|
||||
|
||||
```javascript
|
||||
var App = React.createClass({
|
||||
clearText() {
|
||||
this._textInput.setNativeProps({text: ''});
|
||||
},
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<TextInput ref={component => this._textInput = component}
|
||||
style={styles.textInput} />
|
||||
<TouchableOpacity onPress={this.clearText}>
|
||||
<Text>Clear text</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
});
|
||||
```
|
||||
[Run this example](https://rnplay.org/plays/pOI9bA)
|
||||
|
||||
## 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. [See this example](https://rnplay.org/apps/bp1DvQ)
|
||||
for a demonstration of what can happen if these two collide - notice
|
||||
the jerky animation each 250ms when `setState` triggers a re-render.
|
||||
|
||||
## setNativeProps & shouldComponentUpdate
|
||||
|
||||
By [intelligently applying
|
||||
`shouldComponentUpdate`](https://facebook.github.io/react/docs/advanced-performance.html#avoiding-reconciling-the-dom)
|
||||
you can avoid the unnecessary overhead involved in reconciling unchanged
|
||||
component subtrees, to the point where it may be performant enough to
|
||||
use `setState` instead of `setNativeProps`.
|
@ -4,7 +4,7 @@ title: Native UI Components (iOS)
|
||||
layout: docs
|
||||
category: Guides
|
||||
permalink: docs/nativecomponentsios.html
|
||||
next: linking-libraries
|
||||
next: direct-manipulation
|
||||
---
|
||||
|
||||
There are tons of native UI widgets out there ready to be used in the latest apps - some of them are part of the platform, others are available as third-party libraries, and still more might be in use in your very own portfolio. React Native has several of the most critical platform components already wrapped, like `ScrollView` and `TextInput`, but not all of them, and certainly not ones you might have written yourself for a previous app. Fortunately, it's quite easy to wrap up these existing components for seamless integration with your React Native application.
|
||||
|
Loading…
x
Reference in New Issue
Block a user