feat: accept a wider range of React components

This commit is contained in:
mathuo 2024-02-09 20:15:14 +00:00
parent 30c7fa2c6a
commit 874da1c7d9
No known key found for this signature in database
GPG Key ID: C6EEDEFD6CA07281
2 changed files with 35 additions and 10 deletions

View File

@ -1,4 +1,4 @@
import { ReactPart } from '../react'; import { ReactPart, isReactComponent } from '../react';
import * as React from 'react'; import * as React from 'react';
import { render, screen, act } from '@testing-library/react'; import { render, screen, act } from '@testing-library/react';
@ -40,6 +40,25 @@ describe('react', () => {
expect(screen.getByTestId('valueB').textContent).toBe('22'); expect(screen.getByTestId('valueB').textContent).toBe('22');
}); });
}); });
describe('isReactElement', () => {
test('functional component', () => {
const FunctionalComponent: React.FC = () => <div />;
expect(isReactComponent(FunctionalComponent)).toBeTruthy();
});
test('React.memo component', () => {
const memoComponent = React.memo(() => <div />);
expect(isReactComponent(memoComponent)).toBeTruthy();
});
test('forward ref component', () => {
const ForwardRefComponent = React.forwardRef((props, ref) => (
<div />
));
expect(isReactComponent(ForwardRefComponent)).toBeTruthy();
});
});
}); });
const Component = (props: TestInterface) => { const Component = (props: TestInterface) => {

View File

@ -102,14 +102,14 @@ export class ReactPart<P extends object, C extends object = {}>
throw new Error('invalid operation: resource is already disposed'); throw new Error('invalid operation: resource is already disposed');
} }
if (typeof this.component !== 'function') { if (!isReactComponent(this.component)) {
/** /**
* we know this isn't a React.FunctionComponent so throw an error here. * we know this isn't a React.FunctionComponent so throw an error here.
* if we do not intercept this the React library will throw a very obsure error * if we do not intercept then React library will throw a very obsure error
* for the same reason, at least at this point we will emit a sensible stacktrace. * for the same reason... at least at this point we will emit a sensible stacktrace.
*/ */
throw new Error( throw new Error(
'Invalid Operation. dockview only supports React Functional Components.' 'Dockview: Only React.memo(...), React.ForwardRef(...) and functional components are accepted as components'
); );
} }
@ -192,9 +192,15 @@ export const usePortalsLifecycle: PortalLifecycleHook = () => {
return [portals, addPortal]; return [portals, addPortal];
}; };
// it does the job...
export function isReactElement( export function isReactComponent(component: any): boolean {
element: unknown /**
): element is React.ReactElement { * Yes, we could use "react-is" but that would introduce an unwanted peer dependency
return !!(element as React.ReactElement)?.type; * so for now we will check in a rather crude fashion...
*/
return (
typeof component === 'function' /** Functional Componnts */ ||
!!(component as React.ExoticComponent)
?.$$typeof /** React.memo(...) Components */
);
} }