【问题标题】:React HOC with Typescript and React Hooks使用 Typescript 和 React Hooks 反应 HOC
【发布时间】:2020-09-24 17:51:38
【问题描述】:

我正在尝试在我的 HOC 中使用 React Hooks 将一个事件传递给父级,说明该组件何时位于 Viewport 上,以便使用 React Intersection Observer 延迟加载此组件数据,但我正在努力解决以下错误: Type '(props: IStockProps) => JSX.Element | JSX.Element[]' is not assignable to type 'FC<IStockProps>'.

我最近开始使用 Typescript,但我仍在学习如何阅读这些错误,有时可能会有点混乱。这是我的代码

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} />;
  };

组件:

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>
      ));
};

仪表板

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>
    </>
  );
};

编辑:所以,我发现这个错误是由组件引起的,而不是由 HOC 引起的,所以我修复了它,但现在我遇到了新的错误,更可怕

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

所有这些错误都是关于尝试将带有接口IHocPropsprops 传递给接收带有接口IStockPropsprops 的组件,对吧?有没有办法让我的 HOC 接收更通用的可能类型,以便我可以将它与任何其他组件一起使用?

【问题讨论】:

    标签: javascript reactjs typescript react-hooks


    【解决方案1】:

    嗯,Typescript 编译器实际上是在告诉你哪里出了问题,但是当你刚开始使用 Typescript 时看不到它是可以理解的。

    问题出在你的Component上,你此刻返回,JSX Element | JSXElement[] 正如编译器所说。 React.FC 必须返回有效的“React 元素”。 如果你改变这个:

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

    到:

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

    您的代码应该可以工作。 Key 是在 React.Fragment 中包装返回

    但是,如果我可能会问,您为什么要使用 HOC,尤其是使用钩子?钩子的重点是不使用 HOC。

    你可以用钩子做这个,把逻辑提取到钩子里

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

    并在你的组件中使用它

    const onEnterView = () => {} // whatever you want
    const lazyStockedRef = useLazyLoader(onEnterView)
    return <StockSituation ref={lazyStockedRef} /* other props here */ />
    

    【讨论】:

    • 关于错误,我意识到“反应元素”问题,但后来我在编辑中遇到了其他错误。关于 HOC,我考虑过使用 HOC,因为我没有想到任何其他方式与其他组件共享逻辑。我曾想象使用钩子,我必须将钩子插入每个组件。但是我可以从您的回答中看出,使用 Hooks 会容易得多,而且我让一切都复杂化了。感谢您的回答!
    • 关于编辑中的错误消息,您将具有IStockPropsStockSituation 库存情况发送给接受IHocProps 组件的HOC。可能只是将HOC 更改为接受IStockProps可以解决您的问题,但仅靠手很难判断。
    猜你喜欢
    • 2019-05-02
    • 2018-09-11
    • 2020-01-06
    • 2019-07-17
    • 2021-04-03
    • 1970-01-01
    • 2018-11-09
    • 1970-01-01
    • 2021-05-11
    相关资源
    最近更新 更多