Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit ef52e22

Browse files
committed
Adding ReactNative.setNativeProps that takes a ref
1 parent 4f4aa69 commit ef52e22

File tree

5 files changed

+223
-1
lines changed

5 files changed

+223
-1
lines changed

packages/react-native-renderer/src/ReactFabric.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import NativeMethodsMixin from './NativeMethodsMixin';
3232
import ReactNativeComponent from './ReactNativeComponent';
3333
import {getClosestInstanceFromNode} from './ReactFabricComponentTree';
3434
import {getInspectorDataForViewTag} from './ReactNativeFiberInspector';
35+
import {setNativeProps} from './ReactNativeSetNativeProps';
3536

3637
import ReactSharedInternals from 'shared/ReactSharedInternals';
3738
import getComponentName from 'shared/getComponentName';
@@ -104,6 +105,8 @@ const ReactFabric: ReactFabricType = {
104105

105106
findNodeHandle,
106107

108+
setNativeProps,
109+
107110
render(element: React$Element<any>, containerTag: any, callback: ?Function) {
108111
let root = roots.get(containerTag);
109112

packages/react-native-renderer/src/ReactNativeRenderer.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import NativeMethodsMixin from './NativeMethodsMixin';
3838
import ReactNativeComponent from './ReactNativeComponent';
3939
import {getClosestInstanceFromNode} from './ReactNativeComponentTree';
4040
import {getInspectorDataForViewTag} from './ReactNativeFiberInspector';
41+
import {setNativeProps} from './ReactNativeSetNativeProps';
4142

4243
import ReactSharedInternals from 'shared/ReactSharedInternals';
4344
import getComponentName from 'shared/getComponentName';
@@ -116,6 +117,8 @@ const ReactNativeRenderer: ReactNativeType = {
116117

117118
findNodeHandle,
118119

120+
setNativeProps,
121+
119122
render(element: React$Element<any>, containerTag: any, callback: ?Function) {
120123
let root = roots.get(containerTag);
121124

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow
8+
*/
9+
10+
import {create} from './ReactNativeAttributePayload';
11+
import {warnForStyleProps} from './NativeMethodsMixinUtils';
12+
13+
import ReactSharedInternals from 'shared/ReactSharedInternals';
14+
import getComponentName from 'shared/getComponentName';
15+
import warningWithoutStack from 'shared/warningWithoutStack';
16+
17+
// Module provided by RN:
18+
import UIManager from 'UIManager';
19+
20+
const ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner;
21+
22+
export function setNativeProps(handle, nativeProps: Object) {
23+
if (handle._nativeTag == null) {
24+
warningWithoutStack(
25+
handle._nativeTag != null,
26+
"setNativeProps was called on a ref that isn't a " +
27+
'native component. Use React.forwardRef to get access to the underlying native component',
28+
);
29+
return;
30+
}
31+
32+
if (__DEV__) {
33+
warnForStyleProps(nativeProps, handle.viewConfig.validAttributes);
34+
}
35+
36+
const updatePayload = create(nativeProps, handle.viewConfig.validAttributes);
37+
38+
// Avoid the overhead of bridge calls if there's no update.
39+
// This is an expensive no-op for Android, and causes an unnecessary
40+
// view invalidation for certain components (eg RCTTextInput) on iOS.
41+
if (updatePayload != null) {
42+
UIManager.updateView(
43+
handle._nativeTag,
44+
handle.viewConfig.uiViewClassName,
45+
updatePayload,
46+
);
47+
}
48+
}

packages/react-native-renderer/src/__tests__/ReactFabric-test.internal.js

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ describe('ReactFabric', () => {
167167
expect(FabricUIManager.__dumpHierarchyForJestTestsOnly()).toMatchSnapshot();
168168
});
169169

170-
it('should not call UIManager.updateView from setNativeProps for properties that have not changed', () => {
170+
it('should not call UIManager.updateView from ref.setNativeProps for properties that have not changed', () => {
171171
const View = createReactNativeComponentClass('RCTView', () => ({
172172
validAttributes: {foo: true},
173173
uiViewClassName: 'RCTView',
@@ -214,6 +214,90 @@ describe('ReactFabric', () => {
214214
});
215215
});
216216

217+
it('should be able to setNativeProps on native refs', () => {
218+
const View = createReactNativeComponentClass('RCTView', () => ({
219+
validAttributes: {foo: true},
220+
uiViewClassName: 'RCTView',
221+
}));
222+
223+
UIManager.updateView.mockReset();
224+
225+
let viewRef;
226+
ReactFabric.render(
227+
<View
228+
foo="bar"
229+
ref={ref => {
230+
viewRef = ref;
231+
}}
232+
/>,
233+
11,
234+
);
235+
236+
expect(UIManager.updateView).not.toBeCalled();
237+
ReactFabric.setNativeProps(viewRef, {foo: 'baz'});
238+
expect(UIManager.updateView).toHaveBeenCalledTimes(1);
239+
expect(UIManager.updateView).toHaveBeenCalledWith(
240+
expect.any(Number),
241+
'RCTView',
242+
{foo: 'baz'},
243+
);
244+
});
245+
246+
it('should warn and no-op if calling setNativeProps on non native refs', () => {
247+
const View = createReactNativeComponentClass('RCTView', () => ({
248+
validAttributes: {foo: true},
249+
uiViewClassName: 'RCTView',
250+
}));
251+
252+
class BasicClass extends React.Component {
253+
render() {
254+
return <React.Fragment />;
255+
}
256+
}
257+
258+
class Subclass extends ReactFabric.NativeComponent {
259+
render() {
260+
return <View />;
261+
}
262+
}
263+
264+
const CreateClass = createReactClass({
265+
mixins: [NativeMethodsMixin],
266+
render: () => {
267+
return <View />;
268+
},
269+
});
270+
271+
[BasicClass, Subclass, CreateClass].forEach(Component => {
272+
UIManager.updateView.mockReset();
273+
274+
let viewRef;
275+
ReactFabric.render(
276+
<Component
277+
foo="bar"
278+
ref={ref => {
279+
viewRef = ref;
280+
}}
281+
/>,
282+
11,
283+
);
284+
285+
expect(UIManager.updateView).not.toBeCalled();
286+
expect(() => {
287+
ReactFabric.setNativeProps(viewRef, {foo: 'baz'});
288+
}).toWarnDev(
289+
[
290+
"Warning: setNativeProps was called on a ref that isn't a " +
291+
'native component. Use React.forwardRef to get access ' +
292+
'to the underlying native component',
293+
],
294+
{withoutStack: true},
295+
);
296+
297+
expect(UIManager.updateView).not.toBeCalled();
298+
});
299+
});
300+
217301
it('returns the correct instance and calls it in the callback', () => {
218302
const View = createReactNativeComponentClass('RCTView', () => ({
219303
validAttributes: {foo: true},

packages/react-native-renderer/src/__tests__/ReactNativeMount-test.internal.js

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,90 @@ describe('ReactNative', () => {
145145
});
146146
});
147147

148+
it('should be able to setNativeProps on native refs', () => {
149+
const View = createReactNativeComponentClass('RCTView', () => ({
150+
validAttributes: {foo: true},
151+
uiViewClassName: 'RCTView',
152+
}));
153+
154+
UIManager.updateView.mockReset();
155+
156+
let viewRef;
157+
ReactNative.render(
158+
<View
159+
foo="bar"
160+
ref={ref => {
161+
viewRef = ref;
162+
}}
163+
/>,
164+
11,
165+
);
166+
167+
expect(UIManager.updateView).not.toBeCalled();
168+
ReactNative.setNativeProps(viewRef, {foo: 'baz'});
169+
expect(UIManager.updateView).toHaveBeenCalledTimes(1);
170+
expect(UIManager.updateView).toHaveBeenCalledWith(
171+
expect.any(Number),
172+
'RCTView',
173+
{foo: 'baz'},
174+
);
175+
});
176+
177+
it('should warn and no-op if calling setNativeProps on non native refs', () => {
178+
const View = createReactNativeComponentClass('RCTView', () => ({
179+
validAttributes: {foo: true},
180+
uiViewClassName: 'RCTView',
181+
}));
182+
183+
class BasicClass extends React.Component {
184+
render() {
185+
return <React.Fragment />;
186+
}
187+
}
188+
189+
class Subclass extends ReactNative.NativeComponent {
190+
render() {
191+
return <View />;
192+
}
193+
}
194+
195+
const CreateClass = createReactClass({
196+
mixins: [NativeMethodsMixin],
197+
render: () => {
198+
return <View />;
199+
},
200+
});
201+
202+
[BasicClass, Subclass, CreateClass].forEach(Component => {
203+
UIManager.updateView.mockReset();
204+
205+
let viewRef;
206+
ReactNative.render(
207+
<Component
208+
foo="bar"
209+
ref={ref => {
210+
viewRef = ref;
211+
}}
212+
/>,
213+
11,
214+
);
215+
216+
expect(UIManager.updateView).not.toBeCalled();
217+
expect(() => {
218+
ReactNative.setNativeProps(viewRef, {foo: 'baz'});
219+
}).toWarnDev(
220+
[
221+
"Warning: setNativeProps was called on a ref that isn't a " +
222+
'native component. Use React.forwardRef to get access ' +
223+
'to the underlying native component',
224+
],
225+
{withoutStack: true},
226+
);
227+
228+
expect(UIManager.updateView).not.toBeCalled();
229+
});
230+
});
231+
148232
it('returns the correct instance and calls it in the callback', () => {
149233
const View = createReactNativeComponentClass('RCTView', () => ({
150234
validAttributes: {foo: true},

0 commit comments

Comments
 (0)