diff --git a/packages/react-is/src/ReactIs.js b/packages/react-is/src/ReactIs.js index e94599474d..13adb68f4f 100644 --- a/packages/react-is/src/ReactIs.js +++ b/packages/react-is/src/ReactIs.js @@ -90,6 +90,23 @@ export function isForwardRef(object: any) { export function isFragment(object: any) { return typeOf(object) === REACT_FRAGMENT_TYPE; } +export function isNode(object: any) { + switch (typeof object) { + case 'number': + case 'string': + case 'undefined': + return true; + case 'boolean': + return !object; + case 'object': + if (Array.isArray(object)) { + return object.every(isNode); + } + return object === null || isElement(object); + default: + return false; + } +} export function isProfiler(object: any) { return typeOf(object) === REACT_PROFILER_TYPE; } diff --git a/packages/react-is/src/__tests__/ReactIs-test.js b/packages/react-is/src/__tests__/ReactIs-test.js index 04c7cec745..737bab405d 100644 --- a/packages/react-is/src/__tests__/ReactIs-test.js +++ b/packages/react-is/src/__tests__/ReactIs-test.js @@ -85,6 +85,26 @@ describe('ReactIs', () => { expect(ReactIs.isContextConsumer(
)).toBe(false); }); + it('should identify nodes', () => { + expect(ReactIs.isNode(
)).toBe(true); + expect(ReactIs.isNode('div')).toBe(true); + expect(ReactIs.isNode(false)).toBe(true); + expect(ReactIs.isNode(true)).toBe(false); + expect(ReactIs.isNode(123)).toBe(true); + expect(ReactIs.isNode(null)).toBe(true); + expect(ReactIs.isNode(undefined)).toBe(true); + expect(ReactIs.isNode([1, 'string',
])).toBe(true); + expect(ReactIs.isNode({})).toBe(false); + + // It should also identify more specific types as nodes + const Context = React.createContext(false); + expect(ReactIs.isNode()).toBe(true); + expect(ReactIs.isNode()).toBe(true); + expect(ReactIs.isNode()).toBe(true); + expect(ReactIs.isNode()).toBe(true); + expect(ReactIs.isNode()).toBe(true); + }); + it('should identify context providers', () => { const Context = React.createContext(false); expect(ReactIs.typeOf()).toBe(ReactIs.ContextProvider);