Skip to content

Commit a8be5a2

Browse files
committed
refactored hoc examples
1 parent 2a45ffc commit a8be5a2

File tree

5 files changed

+73
-61
lines changed

5 files changed

+73
-61
lines changed

README.md

Lines changed: 36 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -450,27 +450,30 @@ import * as React from 'react';
450450
import { Subtract } from 'utility-types';
451451

452452
// These props will be subtracted from original component type
453-
interface WrappedComponentProps {
453+
interface InjectedProps {
454454
count: number;
455455
onIncrement: () => any;
456456
}
457457

458-
export const withState = <P extends WrappedComponentProps>(
459-
WrappedComponent: React.ComponentType<P>
458+
export const withState = <WrappedProps extends InjectedProps>(
459+
WrappedComponent: React.ComponentType<WrappedProps>
460460
) => {
461461
// These props will be added to original component type
462-
interface Props {
462+
type HocProps = Subtract<WrappedProps, InjectedProps> & {
463+
// here you can extend hoc props
463464
initialCount?: number;
464-
}
465-
interface State {
465+
};
466+
type HocState = {
466467
readonly count: number;
467-
}
468+
};
468469

469-
return class WithState extends React.Component<Subtract<P, WrappedComponentProps> & Props, State> {
470+
return class WithState extends React.Component<HocProps, HocState> {
470471
// Enhance component name for debugging and React-Dev-Tools
471472
static displayName = `withState(${WrappedComponent.name})`;
473+
// reference to original wrapped component
474+
static readonly WrappedComponent = WrappedComponent;
472475

473-
readonly state: State = {
476+
readonly state: HocState = {
474477
count: Number(this.props.initialCount) || 0,
475478
};
476479

@@ -479,14 +482,14 @@ export const withState = <P extends WrappedComponentProps>(
479482
}
480483

481484
render() {
482-
const { ...remainingProps } = this.props;
485+
const { ...restProps } = this.props as {};
483486
const { count } = this.state;
484487

485488
return (
486489
<WrappedComponent
487-
{...remainingProps}
488-
count={count}
489-
onIncrement={this.handleIncrement}
490+
{...restProps}
491+
count={count} // injected
492+
onIncrement={this.handleIncrement} // injected
490493
/>
491494
);
492495
}
@@ -519,26 +522,29 @@ Adds error handling using componentDidCatch to any component
519522
520523
```tsx
521524
import * as React from 'react';
522-
import { Subtract } from 'utility-types';
525+
// import { Subtract } from 'utility-types';
526+
type Omit<A, K> = Pick<A, Exclude<keyof A, K>>;
523527

524528
const MISSING_ERROR = 'Error was swallowed during propagation.';
525529

526-
interface WrappedComponentProps {
527-
onReset?: () => any;
530+
interface InjectedProps {
531+
onReset: () => any;
528532
}
529533

530-
export const withErrorBoundary = <P extends WrappedComponentProps>(
531-
WrappedComponent: React.ComponentType<P>
534+
export const withErrorBoundary = <WrappedProps extends InjectedProps>(
535+
WrappedComponent: React.ComponentType<WrappedProps>
532536
) => {
533-
interface Props { }
534-
interface State {
537+
type HocProps = Omit<WrappedProps, keyof InjectedProps> & {
538+
// here you can extend hoc props
539+
};
540+
type HocState = {
535541
readonly error: Error | null | undefined;
536-
}
542+
};
537543

538-
return class WithErrorBoundary extends React.Component<Subtract<P, WrappedComponentProps> & Props, State> {
544+
return class WithErrorBoundary extends React.Component<HocProps, HocState> {
539545
static displayName = `withErrorBoundary(${WrappedComponent.name})`;
540546

541-
readonly state: State = {
547+
readonly state: HocState = {
542548
error: undefined,
543549
};
544550

@@ -556,14 +562,14 @@ export const withErrorBoundary = <P extends WrappedComponentProps>(
556562
}
557563

558564
render() {
559-
const { children, ...remainingProps } = this.props;
565+
const { children, ...restProps } = this.props as { children: React.ReactNode };
560566
const { error } = this.state;
561567

562568
if (error) {
563569
return (
564570
<WrappedComponent
565-
{...remainingProps}
566-
onReset={this.handleReset}
571+
{...restProps}
572+
onReset={this.handleReset} // injected
567573
/>
568574
);
569575
}
@@ -585,15 +591,15 @@ import { ErrorMessage } from '@src/components';
585591
const ErrorMessageWithErrorBoundary =
586592
withErrorBoundary(ErrorMessage);
587593

588-
const ErrorThrower = () => (
589-
<button type="button" onClick={() => { throw new Error(`Catch this!`); }}>
594+
const BrokenButton = () => (
595+
<button type="button" onClick={() => { throw new Error(`Catch me!`); }}>
590596
{`Throw nasty error`}
591597
</button >
592598
);
593599

594600
export default (() => (
595-
<ErrorMessageWithErrorBoundary>
596-
<ErrorThrower />
601+
<ErrorMessageWithErrorBoundary >
602+
<BrokenButton />
597603
</ErrorMessageWithErrorBoundary>
598604
)) as React.SFC<{}>;
599605

playground/src/components/error-message.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as React from 'react';
22

3-
export const ErrorMessage: React.SFC<{ onReset: () => any }> = ({ onReset }) => {
3+
export const ErrorMessage: React.SFC<{ onReset: () => void }> = ({ onReset }) => {
44
return (
55
<div>
66
<h2>{`Sorry there was an unexpected error`}</h2>

playground/src/hoc/with-error-boundary.tsx

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,27 @@
11
import * as React from 'react';
2-
import { Subtract } from 'utility-types';
2+
// import { Subtract } from 'utility-types';
3+
type Omit<A, K> = Pick<A, Exclude<keyof A, K>>;
34

45
const MISSING_ERROR = 'Error was swallowed during propagation.';
56

6-
interface WrappedComponentProps {
7-
onReset?: () => any;
7+
interface InjectedProps {
8+
onReset: () => any;
89
}
910

10-
export const withErrorBoundary = <P extends WrappedComponentProps>(
11-
WrappedComponent: React.ComponentType<P>
11+
export const withErrorBoundary = <WrappedProps extends InjectedProps>(
12+
WrappedComponent: React.ComponentType<WrappedProps>
1213
) => {
13-
interface Props { }
14-
interface State {
14+
type HocProps = Omit<WrappedProps, keyof InjectedProps> & {
15+
// here you can extend hoc props
16+
};
17+
type HocState = {
1518
readonly error: Error | null | undefined;
16-
}
19+
};
1720

18-
return class WithErrorBoundary extends React.Component<Subtract<P, WrappedComponentProps> & Props, State> {
21+
return class WithErrorBoundary extends React.Component<HocProps, HocState> {
1922
static displayName = `withErrorBoundary(${WrappedComponent.name})`;
2023

21-
readonly state: State = {
24+
readonly state: HocState = {
2225
error: undefined,
2326
};
2427

@@ -36,14 +39,14 @@ export const withErrorBoundary = <P extends WrappedComponentProps>(
3639
}
3740

3841
render() {
39-
const { children, ...remainingProps } = this.props;
42+
const { children, ...restProps } = this.props as { children: React.ReactNode };
4043
const { error } = this.state;
4144

4245
if (error) {
4346
return (
4447
<WrappedComponent
45-
{...remainingProps}
46-
onReset={this.handleReset}
48+
{...restProps}
49+
onReset={this.handleReset} // injected
4750
/>
4851
);
4952
}

playground/src/hoc/with-error-boundary.usage.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@ import { ErrorMessage } from '@src/components';
66
const ErrorMessageWithErrorBoundary =
77
withErrorBoundary(ErrorMessage);
88

9-
const ErrorThrower = () => (
10-
<button type="button" onClick={() => { throw new Error(`Catch this!`); }}>
9+
const BrokenButton = () => (
10+
<button type="button" onClick={() => { throw new Error(`Catch me!`); }}>
1111
{`Throw nasty error`}
1212
</button >
1313
);
1414

1515
export default (() => (
16-
<ErrorMessageWithErrorBoundary>
17-
<ErrorThrower />
16+
<ErrorMessageWithErrorBoundary >
17+
<BrokenButton />
1818
</ErrorMessageWithErrorBoundary>
1919
)) as React.SFC<{}>;

playground/src/hoc/with-state.tsx

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,30 @@ import * as React from 'react';
22
import { Subtract } from 'utility-types';
33

44
// These props will be subtracted from original component type
5-
interface WrappedComponentProps {
5+
interface InjectedProps {
66
count: number;
77
onIncrement: () => any;
88
}
99

10-
export const withState = <P extends WrappedComponentProps>(
11-
WrappedComponent: React.ComponentType<P>
10+
export const withState = <WrappedProps extends InjectedProps>(
11+
WrappedComponent: React.ComponentType<WrappedProps>
1212
) => {
1313
// These props will be added to original component type
14-
interface Props {
14+
type HocProps = Subtract<WrappedProps, InjectedProps> & {
15+
// here you can extend hoc props
1516
initialCount?: number;
16-
}
17-
interface State {
17+
};
18+
type HocState = {
1819
readonly count: number;
19-
}
20+
};
2021

21-
return class WithState extends React.Component<Subtract<P, WrappedComponentProps> & Props, State> {
22+
return class WithState extends React.Component<HocProps, HocState> {
2223
// Enhance component name for debugging and React-Dev-Tools
2324
static displayName = `withState(${WrappedComponent.name})`;
25+
// reference to original wrapped component
26+
static readonly WrappedComponent = WrappedComponent;
2427

25-
readonly state: State = {
28+
readonly state: HocState = {
2629
count: Number(this.props.initialCount) || 0,
2730
};
2831

@@ -31,14 +34,14 @@ export const withState = <P extends WrappedComponentProps>(
3134
}
3235

3336
render() {
34-
const { ...remainingProps } = this.props;
37+
const { ...restProps } = this.props as {};
3538
const { count } = this.state;
3639

3740
return (
3841
<WrappedComponent
39-
{...remainingProps}
40-
count={count}
41-
onIncrement={this.handleIncrement}
42+
{...restProps}
43+
count={count} // injected
44+
onIncrement={this.handleIncrement} // injected
4245
/>
4346
);
4447
}

0 commit comments

Comments
 (0)