'React HOC FC with correct Typescript type?

Another just starting out in typescript posts. I have read numerous posts on typescript HOC's and Functional Components replacing the class based components. but i continue to struggle with this.

i want a HOC that is connect()ed to the redux store that can be used to produce a new components that is also connected to the store.

philosophy behind this mechanic is, write a base (hoc) component that updates its core logic from 'mapStateToProps' and 'dispatchToProps' and the component that is wrapped by the HOC does the same in its 'own space' (could not think of a better way to explain this) ultimately resulting in less repeating code.

Maybe i am thinking too much in perspective of a class based approach.

this is what i came up with for now.

the HOC:

import React, {ComponentType}  from 'react';
import { connect } from 'react-redux';

export interface IPixiHoc{
    id:number,
}

const PixiHoc = <T extends IPixiHoc>(Component:ComponentType<T>)  =>{

    const NewComponent = (props:T) =>{

        return (
        <div className="layer">
            <Component {...props}></Component>
        </div>
        );
    
    }

    return NewComponent;

}

const mapStateToProps = (state:any) => {
    return {
    }
}

export default connect(mapStateToProps)(PixiHoc)

results in the following error:

Argument of type '(Component: ComponentType) => JSX.Element' is not assignable to parameter of type 'JSXElementConstructor<Matching<DispatchProp, ComponentType>>'. Type '(Component: ComponentType) => JSX.Element' is not assignable to type '(props: Matching<DispatchProp, ComponentType>) => ReactElement<any, any> | null'. Types of parameters 'Component' and 'props' are incompatible. Type 'Matching<DispatchProp, ComponentType>' is not assignable to type 'ComponentType'. Type 'Matching<DispatchProp, ComponentClass<IPixiHoc, any>>' is not assignable to type 'ComponentType'. Type 'Matching<DispatchProp, ComponentClass<IPixiHoc, any>>' is not assignable to type 'ComponentClass<IPixiHoc, any>'. Type 'Matching<DispatchProp, ComponentClass<IPixiHoc, any>>' provides no match for the signature 'new (props: IPixiHoc, context?: any): Component<IPixiHoc, any, any>'.

Because of my utter lack of understanding i find it very difficult to read this error.

the wrapped component is a follow:

import { useEffect, PropsWithChildren } from 'react'
import PixiHoc, {IPixiHoc} from './PixiHoc'


interface ITest extends IPixiHoc{
    color:string,
}

const WrappedComponent = (props:PropsWithChildren<ITest>) => {

    useEffect(()=>{
           console.log(props.color)
           console.log(props.id)
    },[])

    return (
        <div>wrapped component {props.id}-{props.color}</div>
    );
}

const WithHoc = PixiHoc(WrappedComponent)
export default WithHoc;

  • const PixiHoc:any
  • const WrappedComponent:any

Resolves the issue but defies the need to use typescript in the first place i guess.

  • What should be the correct type returned by the connected HOC to satisfy the TS compiler?
  • What should be the correct type returned by the connected Wrapped component to satisfy the TS compiler?
  • Should i stay away from HOC's and use hooks? How would one implement this scenario with hooks only?

Thanks for reading.

Kind regards, Wouter



Solution 1:[1]

i did some more research and found out there are some replacement (redux)hooks for the connect() function.

If i understood correctly:

if you want to connect the HOC itself to the store and you want some flexibility in terms of not have to infer multiple types for the props use useSelector() / useDispatch() redux hook.

the HOC:

export interface IPixiHoc{
    id:string,
    layers?:any[],
    
}

const PixiHOC = <T extends IPixiHoc>(Component:ComponentType<T>)  =>{
    
    
    const Wrapper:FunctionComponent<T> = (props:T) =>{

        // get from redux store (same as mapStateToProps)
        const layers = useSelector( (state:RootState) => state.root.layers)
        
        // also look into useDispatch()

        useEffect(()=>{
            console.log("layers was updated")
        },[layers])

        return (
            <div>
                <div>hello{JSON.stringify(layers.length)}</div>
                <Component {...props}></Component>
            </div>
        );
    
    }

    
    // no need for connecting this component with connect()
    return Wrapper;

}


export default PixiHOC;

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Cello