Skip to content

Commit 0881b0f

Browse files
committed
Merge pull request #1656 from brentvatne/set-native-props-docs
[Docs] Add DirectManipluation guide to explain setNativeProps
2 parents 7733c77 + e2c6060 commit 0881b0f

File tree

2 files changed

+243
-1
lines changed

2 files changed

+243
-1
lines changed

docs/DirectManipulation.md

Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
---
2+
id: direct-manipulation
3+
title: Direct Manipulation
4+
layout: docs
5+
category: Guides
6+
permalink: docs/direct-manipulation.html
7+
next: linking-libraries
8+
---
9+
10+
It is sometimes necessary to make changes directly to a component
11+
without using state/props to trigger a re-render of the entire subtree.
12+
When using React in the browser for example, you sometimes need to
13+
directly modify a DOM node, and the same is true for views in mobile
14+
apps. `setNativeProps` is the React Native equivalent to setting
15+
properties directly on a DOM node.
16+
17+
> Use setNativeProps when frequent re-rendering creates a performance bottleneck
18+
>
19+
> Direct manipulation will not be a tool that you reach for
20+
> frequently; you will typically only be using it for creating
21+
> continuous animations to avoid the overhead of rendering the component
22+
> hierarchy and reconciling many views. `setNativeProps` is imperative
23+
> and stores state in the native layer (DOM, UIView, etc.) and not
24+
> within your React components, which makes your code more difficult to
25+
> reason about. Before you use it, try to solve your problem with `setState`
26+
> and [shouldComponent](react-native/docs/direct-manipulation.html#setnativeprops-shouldcomponentupdate).
27+
28+
## setNativeProps with TouchableOpacity
29+
30+
[TouchableOpacity](https://github.com/facebook/react-native/blob/master/Libraries/Components/Touchable/TouchableOpacity.js)
31+
uses `setNativeProps` internally to update the opacity of its child
32+
component:
33+
34+
```javascript
35+
setOpacityTo: function(value) {
36+
// Redacted: animation related code
37+
this.refs[CHILD_REF].setNativeProps({
38+
opacity: value
39+
});
40+
},
41+
```
42+
43+
This allows us to write the following code and know that the child will
44+
have its opacity updated in response to taps, without the child having
45+
any knowledge of that fact or requiring any changes to its implementation:
46+
47+
```javascript
48+
<TouchableOpacity onPress={this._handlePress}>
49+
<View style={styles.button}>
50+
<Text>Press me!</Text>
51+
</View>
52+
<TouchableOpacity>
53+
```
54+
55+
Let's imagine that `setNativeProps` was not available. One way that we
56+
might implement it with that constraint is to store the opacity value
57+
in the state, then update that value whenever `onPress` is fired:
58+
59+
```javascript
60+
getInitialState() {
61+
return { myButtonOpacity: 1, }
62+
},
63+
64+
render() {
65+
return (
66+
<TouchableOpacity onPress={() => this.setState({myButtonOpacity: 0.5})}
67+
onPressOut={() => this.setState({myButtonOpacity: 1})}>
68+
<View style={[styles.button, {opacity: this.state.myButtonOpacity}]}>
69+
<Text>Press me!</Text>
70+
</View>
71+
</TouchableOpacity>
72+
)
73+
}
74+
```
75+
76+
This is computationally intensive compared to the original example -
77+
React needs to re-render the component hierarchy each time the opacity
78+
changes, even though other properties of the view and its children
79+
haven't changed. Usually this overhead isn't a concern but when
80+
performing continuous animations and responding to gestures, judiciously
81+
optimizing your components can improve your animations' fidelity.
82+
83+
If you look at the implementation of `setNativeProps` in
84+
[NativeMethodsMixin.js](https://github.com/facebook/react-native/blob/master/Libraries/ReactIOS/NativeMethodsMixin.js)
85+
you will notice that it is a wrapper around `RCTUIManager.updateView` -
86+
this is the exact same function call that results from re-rendering -
87+
see [receiveComponent in
88+
ReactNativeBaseComponent.js](https://github.com/facebook/react-native/blob/master/Libraries/ReactNative/ReactNativeBaseComponent.js).
89+
90+
## Composite components and setNativeProps
91+
92+
Composite components are not backed by a native view, so you cannot call
93+
`setNativeProps` on them. Consider this example:
94+
95+
```javascript
96+
var MyButton = React.createClass({
97+
render() {
98+
return (
99+
<View>
100+
<Text>{this.props.label}</Text>
101+
</View>
102+
)
103+
},
104+
});
105+
106+
var App = React.createClass({
107+
render() {
108+
return (
109+
<TouchableOpacity>
110+
<MyButton label="Press me!" />
111+
</TouchableOpacity>
112+
)
113+
},
114+
});
115+
```
116+
[Run this example](https://rnplay.org/apps/JXkgmQ)
117+
118+
If you run this you will immediately see this error: `Touchable child
119+
must either be native or forward setNativeProps to a native component`.
120+
This occurs because `MyButton` isn't directly backed by a native view
121+
whose opacity should be set. You can think about it like this: if you
122+
define a component with `React.createClass` you would not expect to be
123+
able to set a style prop on it and have that work - you would need to
124+
pass the style prop down to a child, unless you are wrapping a native
125+
component. Similarly, we are going to forward `setNativeProps` to a
126+
native-backed child component.
127+
128+
#### Forward setNativeProps to a child
129+
130+
All we need to do is provide a `setNativeProps` method on our component
131+
that calls `setNativeProps` on the appropriate child with the given
132+
arguments.
133+
134+
```javascript
135+
var MyButton = React.createClass({
136+
setNativeProps(nativeProps) {
137+
this._root.setNativeProps(nativeProps);
138+
},
139+
140+
render() {
141+
return (
142+
<View ref={component => this._root = component} {...this.props}>
143+
<Text>{this.props.label}</Text>
144+
</View>
145+
)
146+
},
147+
});
148+
```
149+
[Run this example](https://rnplay.org/apps/YJxnEQ)
150+
151+
You can now use `MyButton` inside of `TouchableOpacity`! A sidenote for
152+
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.
153+
154+
You may have noticed that we passed all of the props down to the child
155+
view using `{...this.props}`. The reason for this is that
156+
`TouchableOpacity` is actually a composite component, and so in addition
157+
to depending on `setNativeProps` on its child, it also requires that the
158+
child perform touch handling. To dot this, it passes on [various
159+
props](https://facebook.github.io/react-native/docs/view.html#onmoveshouldsetresponder)
160+
that call back to the `TouchableOpacity` component.
161+
`TouchableHighlight`, in contrast, is backed by a native view only
162+
requires that we implement `setNativeProps`.
163+
164+
## Precomputing style
165+
166+
We learned above that `setNativeProps` is a wrapper around
167+
`RCTUIManager.updateView`, which is also used internally by React to
168+
perform updates on re-render. One important difference is that
169+
`setNativeProps` does not call `precomputeStyle`, which is done
170+
internally by React, and so the `transform` property will not work if
171+
you try to update it manually with `setNativeProps`. To fix this,
172+
you can call `precomputeStyle` on your object first:
173+
174+
```javascript
175+
var precomputeStyle = require('precomputeStyle');
176+
177+
var App = React.createClass({
178+
componentDidMount() {
179+
var nativeProps = precomputeStyle({transform: [{rotate: '45deg'}]});
180+
this._root.setNativeProps(nativeProps);
181+
},
182+
183+
render() {
184+
return (
185+
<View ref={component => this._root = component}
186+
style={styles.container}>
187+
<Text>Precompute style!</Text>
188+
</View>
189+
)
190+
},
191+
});
192+
```
193+
[Run this example](https://rnplay.org/apps/8_mIAA)
194+
195+
## setNativeProps to clear TextInput value
196+
197+
Another very common use case of `setNativeProps` is to clear the value
198+
of a TextInput. The `controlled` prop of TextInput can sometimes drop
199+
characters when the `bufferDelay` is low and the user types very
200+
quickly. Some developers prefer to skip this prop entirely and instead
201+
use `setNativeProps` to directly manipulate the TextInput value when
202+
necessary. For example, the following code demonstrates clearing the
203+
input when you tap a button:
204+
205+
```javascript
206+
var App = React.createClass({
207+
clearText() {
208+
this._textInput.setNativeProps({text: ''});
209+
},
210+
211+
render() {
212+
return (
213+
<View style={styles.container}>
214+
<TextInput ref={component => this._textInput = component}
215+
style={styles.textInput} />
216+
<TouchableOpacity onPress={this.clearText}>
217+
<Text>Clear text</Text>
218+
</TouchableOpacity>
219+
</View>
220+
);
221+
}
222+
});
223+
```
224+
[Run this example](https://rnplay.org/plays/pOI9bA)
225+
226+
## Avoiding conflicts with the render function
227+
228+
If you update a property that is also managed by the render function,
229+
you might end up with some unpredictable and confusing bugs because
230+
anytime the component re-renders and that property changes, whatever
231+
value was previously set from `setNativeProps` will be completely
232+
ignored and overridden. [See this example](https://rnplay.org/apps/bp1DvQ)
233+
for a demonstration of what can happen if these two collide - notice
234+
the jerky animation each 250ms when `setState` triggers a re-render.
235+
236+
## setNativeProps & shouldComponentUpdate
237+
238+
By [intelligently applying
239+
`shouldComponentUpdate`](https://facebook.github.io/react/docs/advanced-performance.html#avoiding-reconciling-the-dom)
240+
you can avoid the unnecessary overhead involved in reconciling unchanged
241+
component subtrees, to the point where it may be performant enough to
242+
use `setState` instead of `setNativeProps`.

docs/NativeComponentsIOS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ title: Native UI Components (iOS)
44
layout: docs
55
category: Guides
66
permalink: docs/nativecomponentsios.html
7-
next: linking-libraries
7+
next: direct-manipulation
88
---
99

1010
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.

0 commit comments

Comments
 (0)