Skip to content

Commit

Permalink
modify logic according to mui#43022 (comment)
Browse files Browse the repository at this point in the history
  • Loading branch information
sai6855 committed Sep 15, 2024
1 parent 348814b commit b013339
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 4 deletions.
19 changes: 19 additions & 0 deletions packages/mui-utils/src/getReactNodeRef/getReactNodeRef.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import getReactNodeRef from '@mui/utils/getReactNodeRef';
import * as React from 'react';

// @ts-expect-error
getReactNodeRef(false);

// @ts-expect-error
getReactNodeRef(null);

// @ts-expect-error
getReactNodeRef(undefined);

// @ts-expect-error
getReactNodeRef(1);

// @ts-expect-error
getReactNodeRef([<div key="1" />, <div key="2" />]);

getReactNodeRef(<div />);
52 changes: 52 additions & 0 deletions packages/mui-utils/src/getReactNodeRef/getReactNodeRef.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { expect } from 'chai';
import getReactNodeRef from '@mui/utils/getReactNodeRef';
import * as React from 'react';

describe('getReactNodeRef', () => {
it('should throw when not used correctly', () => {
expect(() => {
// @ts-expect-error
getReactNodeRef(false);
// @ts-expect-error
}).toThrowMinified(/expects a React element/);

expect(() => {
// @ts-expect-error
getReactNodeRef();
// @ts-expect-error
}).toThrowMinified(/expects a React element/);

expect(() => {
// @ts-expect-error
getReactNodeRef(1);
// @ts-expect-error
}).toThrowMinified(/expects a React element/);
});

it('should return the ref of a React element', () => {
const ref = React.createRef<HTMLDivElement>();
const element = <div ref={ref} />;
expect(getReactNodeRef(element)).to.equal(ref);
});

it('should return null for a fragment', () => {
const element = (
<React.Fragment>
<p>Hello</p>
<p>Hello</p>
</React.Fragment>
);
expect(getReactNodeRef(element)).to.equal(null);
});

it('should return undefined for an array', () => {
const element = [<div key="1" />, <div key="2" />];
// @ts-expect-error
expect(getReactNodeRef(element)).to.equal(undefined);
});

it('should return null for element with no ref', () => {
const element = <div />;
expect(getReactNodeRef(element)).to.equal(null);
});
});
29 changes: 25 additions & 4 deletions packages/mui-utils/src/getReactNodeRef/getReactNodeRef.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,39 @@
import * as React from 'react';
import MuiError from '@mui/internal-babel-macros/MuiError.macro';

/**
* Returns the ref of a React node handling differences between React 19 and older versions.
* It will return null if the node is not a valid React element.
*
* @param element React.ReactNode
* @param element React.ReactElement
* @returns React.Ref<any> | null
*/
export default function getReactNodeRef(element: React.ReactNode): React.Ref<any> | null {
export default function getReactNodeRef(
element: React.ReactElement,
): React.Ref<any> | null | undefined {
if (typeof element !== 'object') {
throw new MuiError('MUI: `getReactNodeRef(element)` expects a React element.');
}

if (Array.isArray(element)) {
return undefined;
}

if (element.type === React.Fragment) {
return null;
}

// 'ref' is passed as prop in React 19, whereas 'ref' is directly attached to children in older versions
return (element as any)?.props?.propertyIsEnumerable('ref')
? (element as any)?.props.ref
const ref = element.props?.propertyIsEnumerable('ref')
? (element.props as any).ref
: // @ts-expect-error element.ref is not included in the ReactElement type
// We cannot check for it, but isValidElement is true at this point
// https://github.com/DefinitelyTyped/DefinitelyTyped/discussions/70189
element.ref;

if (!ref) {
return null;
}

return ref;
}

0 comments on commit b013339

Please sign in to comment.