Search code examples
reactjstypescriptreact-hookseslinttypescript-eslint

Eslint doesn't recognize a FunctionComponent defined inside a function and complains about using a hook inside it


I have a component-creating function:

import useViewportCategory from '../../../hooks/viewport/useViewportCategory'

type CreateViewportRestrictor = (
    displayName: string,
    settings: { showOnDesktop?: boolean, showOnTablet?: boolean, showOnMobile?: boolean },
) => FunctionComponent

const createViewportRestrictor: CreateViewportRestrictor = (
    displayName,
    { showOnDesktop = false, showOnTablet = false, showOnMobile = false },
) => {
    const component: FunctionComponent = ({ children }) => {
        const viewportCategory = useViewportCategory()

        if (viewportCategory === 'DESKTOP' && !showOnDesktop) return null
        if (viewportCategory === 'MOBILE'  && !showOnMobile ) return null
        if (viewportCategory === 'TABLET'  && !showOnTablet ) return null

        return <>{children}</>
    }

    component.displayName = displayName

    return component
}

That I use to generate components for my app (MobileOnly, TabletOnly, etc.).

However eslint complains about the use of the useViewportCategory hook and pretents me from running the app:

React Hook "useViewportCategory" is called in function "component: FunctionComponent" which is neither a React function component or a custom React Hook function react-hooks/rules-of-hooks

Interestingly, casting the component in TypeScript fixes the error:

const component: FunctionComponent = (({ children }) => {
    const viewportCategory = useViewportCategory()

    if (viewportCategory === DESKTOP && !showOnDesktop) return null
    if (viewportCategory === MOBILE  && !showOnMobile ) return null
    if (viewportCategory === TABLET  && !showOnTablet ) return null

    return <>{children}</>
}) as FunctionComponent

What is going on here?
Why does es lint recognize function components defined directly inside module scope but not here?
What can I do to fix this error without type casting / disabling eslint rule?


Solution

  • Try writing your function in PascalCase:

    const Component: FunctionComponent = (({ children }) => {
        const viewportCategory = useViewportCategory()
    
        if (viewportCategory === DESKTOP && !showOnDesktop) return null
        if (viewportCategory === MOBILE  && !showOnMobile ) return null
        if (viewportCategory === TABLET  && !showOnTablet ) return null
    
        return <>{children}</>
    })
    

    Lint rule expects function to be written in PascalCase.