Skip to content

Commit 38ce240

Browse files
authored
[Docs][NA] Add measurements and Direct Manipulation (facebook#4286)
* [Docs][NA] Add measurements and Direct Manipulation * Apply review feedback * Change title of snack player
1 parent f002d69 commit 38ce240

File tree

3 files changed

+185
-0
lines changed

3 files changed

+185
-0
lines changed
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
---
2+
id: direct-manipulation-new-architecture
3+
title: Direct Manipulation
4+
---
5+
6+
import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import constants from '@site/core/TabsConstants';
7+
8+
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.
9+
10+
:::caution
11+
Use `setNativeProps` when frequent re-rendering creates a performance bottleneck!
12+
13+
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.
14+
`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.
15+
16+
Before you use it, try to solve your problem with `setState` and [`shouldComponentUpdate`](https://reactjs.org/docs/optimizing-performance.html#shouldcomponentupdate-in-action).
17+
:::
18+
19+
## setNativeProps to edit TextInput value
20+
21+
Another very common use case of `setNativeProps` is to edit the value of the 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.
22+
23+
For example, the following code demonstrates editing the input when you tap a button:
24+
25+
<Tabs groupId="language" queryString defaultValue={constants.defaultSnackLanguage} values={constants.snackLanguages}>
26+
<TabItem value="javascript">
27+
28+
```SnackPlayer name=setNativeProps%20on%20TextInput&ext=js
29+
import React from 'react';
30+
import {useCallback, useRef} from 'react';
31+
import {
32+
StyleSheet,
33+
TextInput,
34+
Text,
35+
TouchableOpacity,
36+
View,
37+
} from 'react-native';
38+
39+
const App = () => {
40+
const inputRef = useRef(null);
41+
const editText = useCallback(() => {
42+
inputRef.current.setNativeProps({text: 'Edited Text'});
43+
}, []);
44+
45+
return (
46+
<View style={styles.container}>
47+
<TextInput ref={inputRef} style={styles.input} />
48+
<TouchableOpacity onPress={editText}>
49+
<Text>Edit text</Text>
50+
</TouchableOpacity>
51+
</View>
52+
);
53+
};
54+
55+
const styles = StyleSheet.create({
56+
container: {
57+
flex: 1,
58+
alignItems: 'center',
59+
justifyContent: 'center',
60+
},
61+
input: {
62+
height: 50,
63+
width: 200,
64+
marginHorizontal: 20,
65+
borderWidth: 1,
66+
borderColor: '#ccc',
67+
},
68+
});
69+
70+
export default App;
71+
```
72+
73+
</TabItem>
74+
<TabItem value="typescript">
75+
76+
```SnackPlayer name=Clear%20text&ext=tsx
77+
import React from 'react';
78+
import {useCallback, useRef} from 'react';
79+
import {
80+
StyleSheet,
81+
TextInput,
82+
Text,
83+
TouchableOpacity,
84+
View,
85+
} from 'react-native';
86+
87+
const App = () => {
88+
const inputRef = useRef<TextInput>(null);
89+
const editText = useCallback(() => {
90+
inputRef.current?.setNativeProps({text: 'Edited Text'});
91+
}, []);
92+
93+
return (
94+
<View style={styles.container}>
95+
<TextInput ref={inputRef} style={styles.input} />
96+
<TouchableOpacity onPress={editText}>
97+
<Text>Edit text</Text>
98+
</TouchableOpacity>
99+
</View>
100+
);
101+
};
102+
103+
const styles = StyleSheet.create({
104+
container: {
105+
flex: 1,
106+
alignItems: 'center',
107+
justifyContent: 'center',
108+
},
109+
input: {
110+
height: 50,
111+
width: 200,
112+
marginHorizontal: 20,
113+
borderWidth: 1,
114+
borderColor: '#ccc',
115+
},
116+
});
117+
118+
export default App;
119+
```
120+
121+
</TabItem>
122+
</Tabs>
123+
124+
You can use the [`clear`](../textinput#clear) method to clear the `TextInput` which clears the current input text using the same approach.
125+
126+
## Avoiding conflicts with the render function
127+
128+
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.
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Measuring the Layout
2+
3+
Sometimes, you need to measure the current layout to apply some changes to the overall layout or to make decisions and call some specific logic.
4+
5+
React Native provides some native methods to know what are the measurements of the views.
6+
7+
The best way to invoke those methods is in a `useLayoutEffect` hook: this will give you the most recent values for those measurements and it will let you apply changes in the same frame when the measurements are computed.
8+
9+
Typical code will look like this:
10+
11+
```tsx
12+
function AComponent(children) {
13+
const targetRef = React.useRef(null)
14+
15+
useLayoutEffect(() => {
16+
targetRef.current?.measure(({measurements}) => {
17+
//do something with the `measurements`
18+
});
19+
}, [ /* add dependencies here */]);
20+
21+
return (
22+
<View ref={targetRef}>
23+
{children}
24+
<View />
25+
);
26+
}
27+
```
28+
29+
:::note
30+
The methods described here are available on most of the default components provided by React Native. However, they are _not_ available on composite components that aren't directly backed by a native view. This will generally include most components that you define in your own app.
31+
:::
32+
33+
## measure(callback)
34+
35+
Determines the location on screen (`x` and `y`), `width`, and `height` in the viewport of the given view. Returns the values via an async callback. If successful, the callback will be called with the following arguments:
36+
37+
- `x`: the `x` coordinate of the origin (top-left corner) of the measured view in the viewport.
38+
- `y`: the `y` coordinate of the origin (top-left corner) of the measured view in the viewport.
39+
- `width`: the `width` of the view.
40+
- `height`: the `height` of the view.
41+
- `pageX`: the `x` coordinate of the view in the viewport (typically the the whole screen).
42+
- `pageY`: the `y` coordinate of the view in the viewport (typically the the whole screen).
43+
44+
Also the `width` and `height` returned by `measure()` are the `width` and `height` of the component in the viewport.
45+
46+
## measureInWindow(callback)
47+
48+
Determines the location (`x` and `y`) of the given view in the window and returns the values via an async callback. If the React root view is embedded in another native view, this will give you the absolute coordinates. If successful, the callback will be called with the following arguments:
49+
50+
- `x`: the `x` coordinate of the view in the current window.
51+
- `y`: the `y` coordinate of the view in the current window.
52+
- `width`: the `width` of the view.
53+
- `height`: the `height` of the view.

website/sidebars.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,10 @@
8686
"the-new-architecture/custom-cxx-types",
8787
"the-new-architecture/create-module-library"
8888
],
89+
"Native Components": [
90+
"the-new-architecture/direct-manipulation-new-architecture",
91+
"the-new-architecture/layout-measurements"
92+
],
8993
"Android and iOS guides": [
9094
{
9195
"type": "category",

0 commit comments

Comments
 (0)