除了基本组件的其余属性之外,如何指定高阶组件应该接受 属性?

How do I specify that a Higher Order Component should accept a property in addition to the remaining properties of the base component?

我正在尝试学习 React / Redux 在 Typescript 环境中使用高阶组件。我有两个高阶组件:

  1. withId:为基本组件
  2. 生成uniqueId 属性
  3. withErrorListener:在 redux 存储上呈现错误,源自基本组件请求。基本组件应具有从 withId 高阶组件创建的 uniqueId 属性。

NewComponent = withErrorListener(withId(BaseComponent))

const PostsListWithId = withId(PostsListBase);
export const PostsListConnected = connector(withErrorListener(PostsListWithId));

我正在按如下方式使用 HoC:

import * as React from "react";
import { connect, ConnectedProps } from "react-redux";
import { Link } from "react-router-dom";
import { RootState } from "typesafe-actions";

import { Post as PostComponent } from "../components/Post";

import { allPostsAction, createPostsRequest } from "../features/posts/actions";
import { HocComponentProps } from "../higher-order-components/types";
import { Post } from "../features/posts/types";
import { withErrorListener } from "../higher-order-components/withErrorListener";
import { withId } from "../higher-order-components/componentId/withId";

type StateProps = {
  isLoading: boolean;
  posts: Post[];
};

/**
 * Redux dispatch and state mappings
 */
const dispatchProps = {
  fetchPosts: allPostsAction.request
};

const mapStateToProps = (state: RootState): StateProps => ({
  isLoading: state.posts.isLoadingPosts,
  posts: state.posts.posts
});

const connector = connect(
  mapStateToProps,
  dispatchProps
);

type ReduxProps = ConnectedProps<typeof connector>;

/**
 * Component property type definitions
 */
type Props = ReduxProps & HocComponentProps;

/**
 * CourseList component
 */
const PostsListBase = ({
  posts = [],
  uniqueId,
  fetchPosts,
  isLoading
}: Props): JSX.Element => {
  console.log(`PostListBase id :: ${uniqueId}`);

  // dispatch fetch posts action on mount
  React.useEffect(() => {
    console.log(
      `PostListBase dispatching PostsRequest action with id ${uniqueId}`
    );
    fetchPosts(createPostsRequest(uniqueId));
  }, [fetchPosts, uniqueId]);

  if (isLoading) {
    return (
      <>
        <p>Loading...</p>
        <Link to="/">Home</Link>
      </>
    );
  }

  return (
    <div style={{ marginTop: 20, padding: 30 }}>
      <Link to="/">Home</Link>
      <h2>List of post id's</h2>
      {
        <ul>
          {posts.map(element => (
            <li key={element.id}>
              <PostComponent post={element} />
            </li>
          ))}
        </ul>
      }
    </div>
  );
};

const PostsListWithId = withId(PostsListBase);
export const PostsListConnected = connector(withErrorListener(PostsListWithId));

/**
 * Argument of type 'typeof IdHoC' is not assignable to parameter of type 'ComponentType<ExpectedProps>'.
  Type 'typeof IdHoC' is not assignable to type 'ComponentClass<ExpectedProps, any>'.
    Types of parameters 'hocprops' and 'props' are incompatible.
      Type 'ExpectedProps' is missing the following properties from type 'Pick<Props, "posts" | "fetchPosts" | "isLoading">': posts, fetchPosts, isLoadingts(2345)
 */

但是,我收到以下打字稿编译错误:

Argument of type 'typeof IdHoC' is not assignable to parameter of type 'ComponentType<ExpectedProps>'.
  Type 'typeof IdHoC' is not assignable to type 'ComponentClass<ExpectedProps, any>'.
    Types of parameters 'hocprops' and 'props' are incompatible.
      Type 'ExpectedProps' is missing the following properties from type 'Pick<Props, "posts" | "fetchPosts" | "isLoading">': posts, fetchPosts, isLoadingts(2345)

如何正确指定 withErrorListener 高阶组件应接受包含 uniqueId 属性 的类型除了基本组件中的其余属性,在本例中为:{posts、fetchPosts 和 isLoading}?

我目前正尝试在代码中执行此操作,如下所示:

type ExpectedProps = { uniqueId: string };
export const withErrorListener = <BaseProps extends ExpectedProps>(
  BaseComponent: React.ComponentType<BaseProps>
) => {....}

我已经创建了一个 codesandbox 项目,包括高阶组件的源代码,在这里:

认为我已经设法解决了这个问题。下面的评论中提出了一个问题,关于 withId 返回一个包装组件,不包括注入的 id 属性。已删除此约束并接受该评论作为解决方案。另一个问题是消费组件需要连接到 redux 存储的属性来接收从 Api 获取的状态。将高级组件的消费者更新为:

// ensure consuming component is connected to redux store to fulfill it's other property requirements
const PostsListConnectedWithId = connector(withId(PostsListBase));
export const PostsListWithErrorListener = withErrorListener(PostsListConnectedWithId);

打字稿

相对于:

const PostsListWithId = withId(PostsListBase);
export const PostsListWithErrorListener = connector(withErrorListener(PostsListWithId));

将 link 包含到分叉沙箱中,以防其他人遇到类似问题:

我查看了您的 CodeSandbox 示例,我不太了解您的需求。

所以在你的代码中,withId HoC 采用一个基础组件,在你的情况下 PostsListBase,并向它注入 uniqueId prop,然后 return 一个包装组件 不再需要 uniqueId 作为 prop 输入,因为它已经从 withId.

获得了它

然后你把不再想要 uniqueIdWrappedPostsListBase 通过 withErrorListener(WrappedPostsListBase).

这是令人困惑的部分,您指定 withErrorListener 仅接受 需要 一个 uniqueId 道具的基础合成输入。看到冲突了吗?