【问题标题】:Generics with Higher Order Function具有高阶函数的泛型
【发布时间】:2020-03-11 08:32:30
【问题描述】:

我想为我现有的React 之一创建一个返回构造函数的函数 成分。返回值是传入的函数组件的自定义类扩展。

换句话说:

我的高阶函数withObservableStream()的返回值——它返回另一个 function - 采用现有的React 组件(具有类型化的道具)并返回新组件的构造函数 最终应呈现为DOM。因为我只是扩展传入的组件 RxJS 订阅两个组件将共享相同的道具。

类型系统应该:

  • 投诉属性不匹配

我怎样才能做到这一点 - 谁能向我展示“泛型”的魔力?这可能吗?

type Props = Triggers & InitialState;

function App(props: Props) {
  return <button
  onClick={(event) => {
    props.onClick(event);
  }}
  >{props.text} (state: {props.counterX})</button>;
}

// still all good here:
const appInstance = App({...triggers, text: "init", counterX: -1});

const WrappedApp = withObservableStream(
  interval(5000).pipe(
    map((o) => ({counterX: o})),
  ),
  {...triggers},
  {
    // counterX: -1,
    text: "XXX",
  },
)(App);
// type sytem should complain meaningful here:
// 2nd & 3rd parameter of observableStream() should match the constructor signature of App (type Props)
// for both new WrappedApp(...) and render(<WrappedApp...)

stackblitz 上的完整代码示例。

【问题讨论】:

    标签: reactjs typescript rxjs typescript-generics


    【解决方案1】:

    我修改了您的代码以添加所需的类型限制。 基本上你需要将泛型类型添加到withObservableStream的返回函数中:

    withObservableStream.tsx

    import React from "react";
    import Observable from "rxjs";
    
    export default <T extends Observable.Observable<any>, U, V>(observable: T, triggers: U, initialState: V) => {
      type cState = U & V;
      return function(Component: React.ComponentType<cState>) {
    
        return class extends React.Component<cState, V> {
          private subscription: any;
    
          constructor(props: cState) {
            console.log("got props", props);
            super(props);
    
            this.state = {
              ...initialState,
            };
            console.log(this.state);
          }
    
          public componentDidMount() {
            console.log("Subscribing...");
            this.subscription = observable.subscribe((newState) => {
              console.log("setting a new state...", newState);
              this.setState({ ...newState });
              console.log("state: ", this.state);
            });
          }
    
          public componentWillUnmount() {
            console.log("Unsubscribing...");
            this.subscription.unsubscribe();
          }
    
    
          public render() {
            return (
              <Component {...this.props} {...this.state} {...triggers} />
            );
          }
        }
      };
    };
    

    index.tsx

    import React from "react";
    import { render } from "react-dom";
    import { interval } from "rxjs";
    import { map } from "rxjs/operators";
    import withObservableStream from "./withObservableStream";
    
    type Triggers = {
      onClick(event: React.MouseEvent<HTMLButtonElement>): void,
    };
    
    type InitialState = {
      text: string,
      counterX: number,
    };
    
    type Props = Triggers & InitialState;
    
    export function App(props: Props) {
      return <button
      onClick={(event) => {
        props.onClick(event);
      }}
      >{props.text} (state: {props.counterX})</button>;
    }
    
    const triggers: Triggers = { onClick: () => console.log("clicked!!!") };
    
    const appInstance = App({...triggers, text: "init", counterX: -1});
    render(appInstance, document.getElementById("root1"));
    
    const WrappedApp = withObservableStream(
      interval(5000).pipe(
        map((o) => ({counterX: o})),
      ),
      {...triggers},
      {
        counterX: -1,
        text: "XXX",
      },
    )(App);
    
    const appInstance2 = new WrappedApp({...triggers, counterX: -1, text: "23"});
    
    render(<WrappedApp {...triggers} counterX={-1} text="23"/>,  document.getElementById("root1"))
    document.getElementById("root2"));
    

    在此处查看运行代码:stackblitz

    【讨论】:

    • 太棒了,看起来很有希望!你有什么建议我可以将WrappedApp 的实例传递给render() 吗? const appInstance = new WrappedApp(...); render(appInstance, ...);
    • 请直接将相关信息放入答案中;外部链接会随着时间的推移而腐烂。请参阅How to Answer 中的“始终引用重要链接的最相关部分,以防目标站点无法访问或永久脱机”。
    • @YannicHamann 我添加了一个示例,说明如何将其传递给渲染函数。
    猜你喜欢
    • 1970-01-01
    • 2020-08-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多