具有高阶函数的泛型
Generics with Higher Order Function
我想创建一个函数,该函数 return 是我现有 React
之一的构造函数
组件。 return 值是传入函数组件的自定义 class 扩展。
换句话说:
我的高阶函数 withObservableStream()
的 return 值 - 它 return 是另一个
函数 - 采用现有的 React
组件(具有类型化的道具)和 return 一个新组件的构造函数
最终应该渲染到 DOM
中。因为我只是扩展传入的组件
RxJS
订阅两个组件将共享相同的道具。
类型系统应该:
- 抱怨 属性 不匹配
我怎样才能做到这一点 - 谁能向我展示 'genericism' 的魔力?这可能吗?
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 上的完整代码示例。
我修改了您的代码以添加所需的类型限制。
基本上,您需要将通用类型添加到 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。
我想创建一个函数,该函数 return 是我现有 React
之一的构造函数
组件。 return 值是传入函数组件的自定义 class 扩展。
换句话说:
我的高阶函数 withObservableStream()
的 return 值 - 它 return 是另一个
函数 - 采用现有的 React
组件(具有类型化的道具)和 return 一个新组件的构造函数
最终应该渲染到 DOM
中。因为我只是扩展传入的组件
RxJS
订阅两个组件将共享相同的道具。
类型系统应该:
- 抱怨 属性 不匹配
我怎样才能做到这一点 - 谁能向我展示 'genericism' 的魔力?这可能吗?
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 上的完整代码示例。
我修改了您的代码以添加所需的类型限制。
基本上,您需要将通用类型添加到 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。