Files
react-native/Libraries/Utilities/__tests__/setAndForwardRef-test.js
empyrical d6c8f189e7 Introduce 'setAndForwardRef' helper function (#21823)
Summary:
This PR introduces a new helper function called `setAndForwardRef`. It is intended to help with moving components that depend on `NativeMethodsMixin` off of `createReactClass`.

It allows for classes that depend on having a ref to a native component to be able to also forward the native component ref to user code.

Usage is like this:

```js
class MyView extends React.Component {
  _nativeRef = null;
  _setNativeRef = setAndForwardRef({
    getForwardedRef: () => this.props.forwardedRef,
    setLocalRef: ref => {
      this._nativeRef = ref;
    },
  });
  render() {
    return <View ref={this._setNativeRef} />;
  }
}
const MyViewWithRef = React.forwardRef((props, ref) => (
  <MyView {...props} forwardedRef={ref} />
));
module.exports = MyViewWithRef;
```
Pull Request resolved: https://github.com/facebook/react-native/pull/21823

Differential Revision: D10436673

Pulled By: TheSavior

fbshipit-source-id: 32e167bb3ea3234f08d5715168b0e61e4e035a7c
2018-10-17 22:00:21 -07:00

135 lines
3.5 KiB
JavaScript

/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
* @emails oncall+react_native
*/
'use strict';
const React = require('React');
const ReactTestRenderer = require('react-test-renderer');
const setAndForwardRef = require('setAndForwardRef');
describe('setAndForwardRef', () => {
let innerFuncCalled = false;
let outerFuncCalled = false;
class ForwardedComponent extends React.Component<{||}> {
testFunc() {
innerFuncCalled = true;
return true;
}
render() {
return null;
}
}
type Props = $ReadOnly<{|
callFunc?: ?boolean,
forwardedRef: React.Ref<typeof ForwardedComponent>,
|}>;
class TestComponent extends React.Component<Props> {
_nativeRef: ?React.ElementRef<typeof ForwardedComponent> = null;
_setNativeRef = setAndForwardRef({
getForwardedRef: () => this.props.forwardedRef,
setLocalRef: ref => {
this._nativeRef = ref;
},
});
componentDidMount() {
if (this.props.callFunc) {
outerFuncCalled = this._nativeRef && this._nativeRef.testFunc();
}
}
render() {
return <ForwardedComponent ref={this._setNativeRef} />;
}
}
// $FlowFixMe - TODO T29156721 `React.forwardRef` is not defined in Flow, yet.
const TestComponentWithRef = React.forwardRef((props, ref) => (
<TestComponent {...props} forwardedRef={ref} />
));
beforeEach(() => {
innerFuncCalled = false;
outerFuncCalled = false;
});
it('should forward refs (function-based)', () => {
let testRef: ?React.ElementRef<typeof ForwardedComponent> = null;
ReactTestRenderer.create(
<TestComponentWithRef
ref={ref => {
testRef = ref;
}}
/>,
);
const val = testRef && testRef.testFunc();
expect(innerFuncCalled).toBe(true);
expect(val).toBe(true);
});
it('should forward refs (createRef-based)', () => {
const createdRef = React.createRef<typeof ForwardedComponent>();
ReactTestRenderer.create(<TestComponentWithRef ref={createdRef} />);
const val = createdRef.current && createdRef.current.testFunc();
expect(innerFuncCalled).toBe(true);
expect(val).toBe(true);
});
it('should forward refs (string-based)', () => {
class Test extends React.Component<{||}> {
refs: $ReadOnly<{|
stringRef?: ?React.ElementRef<typeof ForwardedComponent>,
|}>;
componentDidMount() {
/* eslint-disable react/no-string-refs */
this.refs.stringRef && this.refs.stringRef.testFunc();
/* eslint-enable react/no-string-refs */
}
render() {
/**
* Can't directly pass the test component to `ReactTestRenderer.create`,
* otherwise it will throw. See:
* https://reactjs.org/warnings/refs-must-have-owner.html#strings-refs-outside-the-render-method
*/
/* eslint-disable react/no-string-refs */
return <TestComponentWithRef ref="stringRef" />;
/* eslint-enable react/no-string-refs */
}
}
ReactTestRenderer.create(<Test />);
expect(innerFuncCalled).toBe(true);
});
it('should be able to use the ref from inside of the forwarding class', () => {
expect(() =>
ReactTestRenderer.create(<TestComponentWithRef callFunc={true} />),
).not.toThrow();
expect(innerFuncCalled).toBe(true);
expect(outerFuncCalled).toBe(true);
});
});