Skip to content

Commit fababf7

Browse files
Backport useRefInit and htmlMain added in major version WIP
1 parent 0ee27d4 commit fababf7

File tree

3 files changed

+93
-2
lines changed

3 files changed

+93
-2
lines changed

lib/hooks.dart

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,46 @@ T useContext<T>(Context<T> context) => ContextHelpers.unjsifyNewContext(React.us
392392
/// ```
393393
///
394394
/// Learn more: <https://reactjs.org/docs/hooks-reference.html#useref>.
395-
Ref<T> useRef<T>([T initialValue]) => Ref.useRefInit(initialValue);
395+
Ref<T> useRef<T>([
396+
// This will eventually be deprecated, but not just yet.
397+
// @Deprecated('Use `useRefInit` instead to create refs with initial values.'
398+
// ' Since the argument to useRefInit is required, it can be used to create a Ref that holds a non-nullable type,'
399+
// ' whereas this function can only create Refs with nullable type arguments.')
400+
T initialValue,
401+
]) =>
402+
useRefInit(initialValue);
403+
404+
/// Returns a mutable [Ref] object with [Ref.current] property initialized to [initialValue].
405+
///
406+
/// Changes to the [Ref.current] property do not cause the containing [DartFunctionComponent] to re-render.
407+
///
408+
/// The returned [Ref] object will persist for the full lifetime of the [DartFunctionComponent].
409+
/// Compare to [createRef] which returns a new [Ref] object on each render.
410+
///
411+
/// > __Note:__ there are two [rules for using Hooks](https://reactjs.org/docs/hooks-rules.html):
412+
/// >
413+
/// > * Only call Hooks at the top level.
414+
/// > * Only call Hooks from inside a [DartFunctionComponent].
415+
///
416+
/// __Example__:
417+
///
418+
/// ```dart
419+
/// UseRefTestComponent(Map props) {
420+
/// final countRef = useRefInit(0);
421+
///
422+
/// handleClick([_]) {
423+
/// ref.current = ref.current + 1;
424+
/// window.alert('You clicked ${ref.current} times!');
425+
/// }
426+
///
427+
/// return react.Fragment({}, [
428+
/// react.button({'onClick': handleClick}, ['Click me!']),
429+
/// ]);
430+
/// }
431+
/// ```
432+
///
433+
/// Learn more: <https://reactjs.org/docs/hooks-reference.html#useref>.
434+
Ref<T> useRefInit<T>(T initialValue) => Ref.useRefInit(initialValue);
396435

397436
/// Returns a memoized version of the return value of [createFunction].
398437
///

lib/react.dart

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1699,8 +1699,11 @@ dynamic li = validateJsApiThenReturn(() => ReactDomComponentFactoryProxy('li'));
16991699
/// The HTML `<link>` `LinkElement`.
17001700
dynamic link = validateJsApiThenReturn(() => ReactDomComponentFactoryProxy('link'));
17011701

1702+
@Deprecated('Use htmlMain instead. To be removed in react 7.0.0.')
1703+
dynamic main = htmlMain;
1704+
17021705
/// The HTML `<main>` `Element`.
1703-
dynamic main = validateJsApiThenReturn(() => ReactDomComponentFactoryProxy('main'));
1706+
dynamic htmlMain = validateJsApiThenReturn(() => ReactDomComponentFactoryProxy('main'));
17041707

17051708
/// The HTML `<map>` `MapElement`.
17061709
dynamic map = validateJsApiThenReturn(() => ReactDomComponentFactoryProxy('map'));

test/hooks_test.dart

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,55 @@ main() {
510510
});
511511
});
512512

513+
group('useRefInit -', () {
514+
DivElement mountNode;
515+
ButtonElement reRenderButton;
516+
Ref initRef;
517+
StateHook<int> renderIndex;
518+
519+
setUpAll(() {
520+
mountNode = DivElement();
521+
522+
final UseRefTest = react.registerFunctionComponent((props) {
523+
initRef = useRefInit(mountNode);
524+
525+
renderIndex = useState(1);
526+
527+
return react.Fragment({}, [
528+
react.p({}, [renderIndex.value]),
529+
react.button({
530+
'ref': (ref) => reRenderButton = ref as ButtonElement,
531+
'onClick': (_) => renderIndex.setWithUpdater((prev) => prev + 1)
532+
}, [
533+
're-render'
534+
]),
535+
]);
536+
});
537+
538+
react_dom.render(UseRefTest({}), mountNode);
539+
});
540+
541+
group('correctly initializes a Ref object', () {
542+
test('with current property set to the initial value given', () {
543+
expect(initRef, isA<Ref>());
544+
expect(initRef.current, mountNode);
545+
});
546+
});
547+
548+
group('the returned Ref', () {
549+
test('will persist even after the component re-renders', () {
550+
expect(renderIndex.value, 1);
551+
expect(initRef.current, mountNode, reason: 'Ref object initially created on first render');
552+
553+
react_test_utils.Simulate.click(reRenderButton);
554+
555+
expect(renderIndex.value, 2);
556+
expect(initRef.current, mountNode,
557+
reason: 'useRef returns the same Ref object on every render for the full lifetime of the component');
558+
});
559+
});
560+
});
561+
513562
group('useMemo -', () {
514563
ReactDartFunctionComponentFactoryProxy UseMemoTest;
515564
StateHook<int> count;

0 commit comments

Comments
 (0)