0

I'm trying to use React Hooks in my HOC to pass an event to parent that says when this component is on Viewport, to lazy load this component data using React Intersection Observer, but I'm struggling with the following error: Type '(props: IStockProps) => JSX.Element | JSX.Element[]' is not assignable to type 'FC<IStockProps>'.

I started recently with Typescript and I'm still getting on how to read these errors and sometimes can be a bit confusing. This are my code

HOC

import React, { useEffect, ComponentType, Component } from "react";
import { useInView } from "react-intersection-observer";

interface IHocProps {
  [key: string]: any;
}


export const LazyLoader = (WrappedComponent: ComponentType<IHocProps>) =>
  function Comp(props: IHocProps) {
    const { onEnterView } = props;
    const [ref, inView] = useInView({
      triggerOnce: true,
    });
    useEffect(() => {
      inView && onEnterView();
    }, [inView]);
    return <WrappedComponent ref={ref} {...props} />;
  };

Component:

import React from "react";
import { Skeleton, Space } from "antd";

interface IStockProps {
  isLoading: boolean;
  stockSituation: object;
}

export const StockSituation: React.FC<IStockProps> = (props: IStockProps) => {
  const { isLoading, stockSituation } = props;
  const renderSkeleton = (
    <>
      <Skeleton paragraph={{ rows: 2 }} active={true} />
      <Space
        style={{ justifyContent: "center" }}
        direction="horizontal"
        align="center"
        size="large"
      >
        <Skeleton.Avatar size={200} active={true} />
        <Skeleton.Avatar size={200} active={true} />
        <Skeleton.Avatar size={200} active={true} />
        <Skeleton.Avatar size={200} active={true} />
      </Space>
    </>
  );
  return isLoading
    ? renderSkeleton
    : Object.keys(stockSituation).map((stock) => (
        <p>{stockSituation[stock as keyof IStockProps["stockSituation"]]}</p>
      ));
};

Dashboard

import React from "react";
import { useSelector, useDispatch } from "react-redux";
import { RootState } from "@store";
import {getStock, IStock} from "@store/dashboard/dashboard";
import { Grid, Row, Col } from "react-flexbox-grid";
import {LazyLoader} from "@components";


  const stock: IStock = useSelector(
    ({ dashboard: { stock } }: RootState) => stock
  );
  const loadInfo = () => {
    console.log("info");
    dispatch(getStock());
  };

  const LazyStockSituation = LazyLoader(StockSituation);

  return (
    <>
      <Header />
      <Grid>
          <Row>
            <Col>
                <LazyStockSituation
                  onEnterView={loadInfo}
                  stockSituation={stock}
                  isLoading={!stock}
                />
              </Panel>
            </Col>
          </Row>
      </Grid>
    </>
  );
};

Edit: So, I figure out that this error was cause by the Component and not by the HOC, so I fix it, but now I'm getting new errors, pretty more scary

Argument of type 'FC<IStockProps>' is not assignable to parameter of type 'ComponentType<IHocProps>'.

Type 'FunctionComponent<IStockProps>' is not assignable to type 'FunctionComponent<IHocProps>'.

Types of parameters 'props' and 'props' are incompatible.

 Type 'PropsWithChildren<IHocProps>' is not assignable to type 'PropsWithChildren<IStockProps>'.

Type 'PropsWithChildren<IHocProps>' is missing the following properties from type 'IStockProps': isLoading, stockSituation

All this errors are about trying to pass props with interface IHocProps to the component that receive props with interface IStockProps, right? Is there a way to make my HOC receive the more generic possible types so I can use it with any other component?

1 Answer 1

1

Well, the Typescript compiler is actually telling you what is wrong, but its understandable to not see when you just started using Typescript.

Issue lies in your Component, you return at the moment, JSX Element | JSXElement[] as the compiler says. React.FC must have valid "React Element" returned. If you change this:

  return isLoading
    ? renderSkeleton
    : Object.keys(stockSituation).map((stock) => (
        <p>{stockSituation[stock as keyof IStockProps["stockSituation"]]}</p>
      ));

to:

return  (
    <>
    {
        isLoading ?
        renderSkeleton : Object.keys(stockSituation).map((stock) => (
            <p>{stockSituation[stock as keyof IStockProps["stockSituation"]]}</p>
          ))
    }
  </>
)

Your code should work. Key is wrapping return inside React.Fragment

But nevertheless, if I may ask, why are you using HOC, especially with hooks? Point of hooks is to not use HOCs.

You can just do this with hook, extract logic into hook

const useLazyLoader = (onEnterView: () => void) => {
    const [ref, inView] = useInView({
      triggerOnce: true,
    });
    useEffect(() => {
      inView && onEnterView();
    }, [inView]);
    return ref
}

And use it in your component

const onEnterView = () => {} // whatever you want
const lazyStockedRef = useLazyLoader(onEnterView)
return <StockSituation ref={lazyStockedRef} /* other props here */ />
Sign up to request clarification or add additional context in comments.

2 Comments

About the error, I realize the "React Element" problem, but then I had other errors that are in the edit. About the HOC, I thought about using HOC because I didn't think of any other way to share the logic with other components. I had imagined that using hooks, I would have to insert the hook into each component. But I can see with your answer that with Hooks it is much easier and I was complicating everything. Thanks for the answer!
About the error messages in edit, you are sending the StockSituation stock situation that has IStockPropsto the HOC that accepts component with IHocProps.Probably just changing the HOC to accept IStockProps would fix your issue but its hard to tell just by hand.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.