Я сделал HOC, который предоставляет реквизиты для сетевых вызовов переданному компоненту, поэтому индикаторы загрузки сетевых вызовов, сообщения об ошибках и т. д. могут обрабатываться HOC.
Код работает как положено машинописи не устраивает.
В моем TestContainer.tsx
я делаю getQuotes
реквизит, который будет введен HOC.
Но когда я использую упакованный экспортированный компонент в моем App.tsx
, он жалуется, что я не указал реквизит, который вводится HOC.
Type error: Property 'getQuotes' is missing in type '{ message: string; }' but required in type 'Readonly<Pick<OwnProps & NetworkProps, "message" | "getQuotes">>'. TS2741
25 |
> 26 | <TestContainer message="lala" />
| ^
27 | </div>
28 | );
29 | }
Приложение.tsx:
<div className="App">
<TestContainer message="lala" />
</div>
это мой ХОС
import * as React from "react";
interface Props {}
interface State {
errors: Error[];
isLoading: boolean;
}
export type FunctionMap = { [key: string]: (...args: any[]) => any };
export const withNetwork = <P extends Props>(
PassedComponent: React.ComponentType<P>,
calls: FunctionMap
) => {
return class NetworkWrapper extends React.Component<
Pick<P, Exclude<keyof P, keyof Props>>,
State
> {
functionMap: FunctionMap;
constructor(props: P) {
super(props);
this.functionMap = Object.keys(calls).reduce(
(prev: FunctionMap, current: string) => ({
...prev,
[current]: this.makeHandler(calls[current])
}),
{}
);
}
state = { errors: [], isLoading: false };
makeHandler = (func: (...orignalArgs: any) => any) => async (
...args: any[]
) => {
try {
this.setState({ isLoading: true });
const result = await func(...args);
this.setState({ isLoading: false });
return result;
} catch (error) {
this.setState({ isLoading: false });
this.setState(prev => ({ errors: [...prev.errors, error] }));
}
};
handleDismissError = () => {
this.setState((prev: State) => {
const [first, ...errors] = prev.errors;
return { errors };
});
};
render() {
const props = this.props as P;
return (
<div>
{this.state.isLoading && <h3>LOADING</h3>}
{this.state.errors.length > 0 && (
<>
<ul>
{this.state.errors.map(error => (
// @ts-ignore
<li>{error.message}</li>
))}
</ul>
<button onClick={this.handleDismissError}>Dismiss</button>
</>
)}
<div>
<PassedComponent {...this.functionMap} {...props} />
</div>
</div>
);
}
};
};
Это TestContainer, использующий HOC
import * as React from "react";
import axios from "axios";
import { withNetwork, FunctionMap } from "./NetworkWrapper";
interface OwnProps {
message: string;
}
interface NetworkProps {
getQuotes: (n: number) => Promise<any>;
}
interface State {
body?: any;
}
class TestContainer extends React.Component<OwnProps & NetworkProps, State> {
componentDidMount = async () => {
const { getQuotes } = this.props;
const data = await getQuotes(1234);
data && this.setState({ body: data });
};
state = { body: undefined };
render() {
const { body } = this.state;
const { message } = this.props;
return (
<div>
<h1>{message}</h1>
<section>
<code>{JSON.stringify(body, null, 2)}</code>
</section>
</div>
);
}
}
const api = {
getQuotes: async (n: number) => {
console.warn({ n });
const result = await axios.get(
"http://example.com/quotes"
);
if (result.status === 200) {
return result.data;
} else {
throw new Error("invalid response");
}
}
};
export default withNetwork(TestContainer, api);