From a78b20c157428273abd6e595826049ddf6ff0e1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E7=88=B1=E5=90=83=E7=99=BD=E8=90=9D?= =?UTF-8?q?=E5=8D=9C?= Date: Thu, 12 Dec 2024 17:29:17 +0800 Subject: [PATCH] fix: isFragment not support React 19 (#604) --- src/Children/toArray.ts | 2 +- src/React/isFragment.ts | 19 +++++++++++++ src/ref.ts | 3 ++- tests/toArray-19.test.tsx | 57 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 src/React/isFragment.ts create mode 100644 tests/toArray-19.test.tsx diff --git a/src/Children/toArray.ts b/src/Children/toArray.ts index 37331cd6..ce3c2bd5 100644 --- a/src/Children/toArray.ts +++ b/src/Children/toArray.ts @@ -1,5 +1,5 @@ +import isFragment from '../React/isFragment'; import React from 'react'; -import { isFragment } from 'react-is'; export interface Option { keepEmpty?: boolean; diff --git a/src/React/isFragment.ts b/src/React/isFragment.ts new file mode 100644 index 00000000..514f1c00 --- /dev/null +++ b/src/React/isFragment.ts @@ -0,0 +1,19 @@ +const REACT_ELEMENT_TYPE_18 = Symbol.for('react.element'); +const REACT_ELEMENT_TYPE_19 = Symbol.for('react.transitional.element'); +const REACT_FRAGMENT_TYPE = Symbol.for('react.fragment'); + +/** + * Compatible with React 18 or 19 to check if node is a Fragment. + */ +export default function isFragment(object: any) { + return ( + // Base object type + object && + typeof object === 'object' && + // React Element type + (object.$$typeof === REACT_ELEMENT_TYPE_18 || + object.$$typeof === REACT_ELEMENT_TYPE_19) && + // React Fragment type + object.type === REACT_FRAGMENT_TYPE + ); +} diff --git a/src/ref.ts b/src/ref.ts index 35170714..a972dff3 100644 --- a/src/ref.ts +++ b/src/ref.ts @@ -1,7 +1,8 @@ import type * as React from 'react'; import { isValidElement } from 'react'; -import { ForwardRef, isFragment, isMemo } from 'react-is'; +import { ForwardRef, isMemo } from 'react-is'; import useMemo from './hooks/useMemo'; +import isFragment from './React/isFragment'; export const fillRef = (ref: React.Ref, node: T) => { if (typeof ref === 'function') { diff --git a/tests/toArray-19.test.tsx b/tests/toArray-19.test.tsx new file mode 100644 index 00000000..2d9f7a06 --- /dev/null +++ b/tests/toArray-19.test.tsx @@ -0,0 +1,57 @@ +import React from 'react'; +import { render } from '@testing-library/react'; +import toArray from '../src/Children/toArray'; + +jest.mock('react', () => { + const react19 = jest.requireActual('react-19'); + return react19; +}); + +jest.mock('react-dom', () => { + const reactDom19 = jest.requireActual('react-dom-19'); + return reactDom19; +}); + +jest.mock('react-dom/client', () => { + const reactDom19Client = jest.requireActual('react-dom-19/client'); + return reactDom19Client; +}); + +jest.mock('react-dom/test-utils', () => { + const reactDom19Test = jest.requireActual('react-dom-19/test-utils'); + return reactDom19Test; +}); + +class UL extends React.Component<{ + children?: React.ReactNode; +}> { + render() { + return
    {this.props.children}
; + } +} + +describe('toArray', () => { + it('Fragment', () => { + const ulRef = React.createRef
    (); + + render( +
      +
    • 1
    • + <> +
    • 2
    • +
    • 3
    • + + + <> +
    • 4
    • +
    • 5
    • + +
      +
    , + ); + + const children = toArray(ulRef.current.props.children); + expect(children).toHaveLength(5); + expect(children.map(c => c.key)).toEqual(['1', '2', '3', '4', '5']); + }); +});