From 9d095c30d1e295b26966e27aad87f5eab82812e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Thu, 6 Jun 2024 14:49:27 +0800 Subject: [PATCH] feat: getNodeRef --- src/ref.ts | 38 ++++++++++++++++++++++++++++++-------- tests/ref.test.tsx | 11 ++++++++++- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/ref.ts b/src/ref.ts index 7495267b..3bf07656 100644 --- a/src/ref.ts +++ b/src/ref.ts @@ -1,5 +1,5 @@ import type * as React from 'react'; -import { isValidElement } from 'react'; +import { isValidElement, version } from 'react'; import { ForwardRef, isFragment, isMemo } from 'react-is'; import useMemo from './hooks/useMemo'; @@ -64,14 +64,36 @@ interface RefAttributes extends React.Attributes { ref: React.Ref; } +function isReactElement(node: React.ReactNode) { + return isValidElement(node) && !isFragment(node); +} + export const supportNodeRef = ( node: React.ReactNode, ): node is React.ReactElement & RefAttributes => { - if (!isValidElement(node)) { - return false; - } - if (isFragment(node)) { - return false; - } - return supportRef(node); + return isReactElement(node) && supportRef(node); }; + +/** + * In React 19. `ref` is not a property from node. + * But a property from `props.ref`. + * To check if `props.ref` exist or fallback to `ref`. + */ +export const getNodeRef: ( + node: React.ReactNode, +) => React.Ref | null = + Number(version.split('.')[0]) >= 19 + ? // >= React 19 + node => { + if (isReactElement(node)) { + return (node as any).props.ref; + } + return null; + } + : // < React 19 + node => { + if (isReactElement(node)) { + return (node as any).ref; + } + return null; + }; diff --git a/tests/ref.test.tsx b/tests/ref.test.tsx index f2fd8d0a..cbcb0ae5 100644 --- a/tests/ref.test.tsx +++ b/tests/ref.test.tsx @@ -1,9 +1,11 @@ /* eslint-disable no-eval */ import { fireEvent, render } from '@testing-library/react'; -import React, { ReactNode } from 'react'; +import type { ReactNode } from 'react'; +import React from 'react'; import useEvent from '../src/hooks/useEvent'; import { composeRef, + getNodeRef, supportNodeRef, supportRef, useComposeRef, @@ -199,4 +201,11 @@ describe('ref', () => { expect(supportNodeRef(refCom) && refCom.ref).toBeTruthy(); }); }); + + it('getNodeRef', () => { + const ref = React.createRef(); + const node =
; + + expect(getNodeRef(node)).toBe(ref); + }); });