Skip to content

Commit bd4a628

Browse files
author
Mansur Isakov
committed
feat: advanced patterns
1 parent a9294f2 commit bd4a628

10 files changed

+48
-28
lines changed

src/08-advanced-patterns/63-lazy-load-component.problem.tsx

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
import { lazy, Suspense, useMemo } from "react";
1+
import { ComponentProps, lazy, Suspense, useMemo } from "react";
22

3-
type Props = {
4-
loader: unknown;
5-
};
3+
type Props<T extends React.ComponentType<any>> = {
4+
loader: () => Promise<{
5+
default: T;
6+
}>;
7+
} & ComponentProps<T>;
68

79
/**
810
* 1. This component is supposed to take a loader function that returns a
@@ -16,7 +18,10 @@ type Props = {
1618
* - You'll need to make this a generic component!
1719
* - React.ComponentProps will come in handy, as will React.ComponentType
1820
*/
19-
function LazyLoad({ loader, ...props }: Props) {
21+
function LazyLoad<C extends React.ComponentType<any>>({
22+
loader,
23+
...props
24+
}: Props<C>) {
2025
const LazyComponent = useMemo(() => lazy(loader), [loader]);
2126

2227
return (

src/08-advanced-patterns/64-render-props.problem.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ interface ModalChildProps {
1717
closeModal: () => void;
1818
}
1919

20-
const Modal = ({ children }: any) => {
20+
const Modal = ({ children }: { children: React.FC<ModalChildProps> }) => {
2121
const [isOpen, setIsOpen] = useState(false);
2222

2323
return (
@@ -31,7 +31,7 @@ const Modal = ({ children }: any) => {
3131
<div>
3232
<h1>Modal</h1>
3333
</div>,
34-
document.getElementById("modal-root")!,
34+
document.getElementById("modal-root")!
3535
)}
3636
</>
3737
);

src/08-advanced-patterns/64.5-record-of-components-with-same-props.problem.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { Equal, Expect } from "../helpers/type-utils";
22

33
type InputProps = React.ComponentProps<"input">;
44

5+
type InputType = "text" | "number" | "password";
6+
57
/**
68
* All these components take the same props!
79
*
@@ -12,7 +14,7 @@ type InputProps = React.ComponentProps<"input">;
1214
*
1315
* Hint: Record and satisfies will come in handy.
1416
*/
15-
const COMPONENTS = {
17+
const COMPONENTS: Record<InputType, React.FC<InputProps>> = {
1618
text: (props) => {
1719
return <input {...props} type="text" />;
1820
},
@@ -24,7 +26,7 @@ const COMPONENTS = {
2426
},
2527
};
2628

27-
export const Input = (props: unknown) => {
29+
export const Input = (props: { type: InputType } & InputProps) => {
2830
const Component = COMPONENTS[props.type];
2931
return <Component {...props} />;
3032
};

src/08-advanced-patterns/66-forward-ref-as-local-function.problem.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ import { Equal, Expect } from "../helpers/type-utils";
66
* give fixedForwardRef a type signature that allows it to
77
* work with the example below.
88
*/
9-
function fixedForwardRef(
10-
render: (props: any, ref: any) => any,
11-
): (props: any) => any {
9+
10+
function fixedForwardRef<T, P = {}>(
11+
render: (props: P, ref: React.Ref<T>) => React.ReactNode
12+
): (props: P & React.RefAttributes<T>) => React.ReactNode {
1213
return forwardRef(render) as any;
1314
}
1415

@@ -19,7 +20,7 @@ type Props<T> = {
1920

2021
export const Table = <T,>(
2122
props: Props<T>,
22-
ref: ForwardedRef<HTMLTableElement>,
23+
ref: ForwardedRef<HTMLTableElement>
2324
) => {
2425
return <table ref={ref} />;
2526
};

src/08-advanced-patterns/67-hoc.problem.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { Router, useRouter } from "fake-external-lib";
22

3-
export const withRouter = (Component: any) => {
4-
const NewComponent = (props: any) => {
3+
export const withRouter = <T,>(Component: React.FC<T>) => {
4+
const NewComponent = (props: Omit<T, "router">) => {
55
const router = useRouter();
6-
return <Component {...props} router={router} />;
6+
return <Component {...(props as T)} router={router} />;
77
};
88

99
NewComponent.displayName = `withRouter(${Component.displayName})`;

src/08-advanced-patterns/67.5-hoc-for-generic-components.problem.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,21 @@
11
import { Router, useRouter } from "fake-external-lib";
22
import { Equal, Expect } from "../helpers/type-utils";
33

4-
export const withRouter = <TProps,>(Component: React.ComponentType<TProps>) => {
4+
export const withRouter = <TProps,>(
5+
Component: (props: TProps) => React.ReactNode
6+
): ((props: Omit<TProps, "router">) => React.ReactNode) => {
57
const NewComponent = (props: Omit<TProps, "router">) => {
68
const router = useRouter();
79
return <Component {...(props as TProps)} router={router} />;
810
};
911

10-
NewComponent.displayName = `withRouter(${Component.displayName})`;
12+
NewComponent.displayName = `withRouter(${
13+
(
14+
Component as {
15+
displayName?: string;
16+
}
17+
).displayName
18+
})`;
1119

1220
return NewComponent;
1321
};

src/08-advanced-patterns/69-as-prop.problem.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,12 @@
2424

2525
import { Equal, Expect } from "../helpers/type-utils";
2626

27-
export const Wrapper = (props: any) => {
28-
const Comp = props.as;
27+
export const Wrapper = <TAs extends keyof JSX.IntrinsicElements>(
28+
props: {
29+
as: TAs;
30+
} & React.ComponentProps<TAs>
31+
) => {
32+
const Comp = props.as as string;
2933
return <Comp {...(props as any)}></Comp>;
3034
};
3135

src/08-advanced-patterns/69-as-prop.solution.2.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Equal, Expect } from "../helpers/type-utils";
33
export const Wrapper = <TAs extends keyof JSX.IntrinsicElements>(
44
props: {
55
as: TAs;
6-
} & React.ComponentProps<TAs>,
6+
} & React.ComponentProps<TAs>
77
) => {
88
const Comp = props.as as string;
99

src/08-advanced-patterns/70-as-prop-with-custom-components.problem.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from "react";
1+
import React, { ElementType } from "react";
22
import { Equal, Expect } from "../helpers/type-utils";
33

44
/**
@@ -17,10 +17,10 @@ import { Equal, Expect } from "../helpers/type-utils";
1717
* - ComponentPropsWithRef
1818
* - ComponentProps
1919
*/
20-
export const Wrapper = <TAs extends keyof JSX.IntrinsicElements>(
20+
export const Wrapper = <TAs extends ElementType>(
2121
props: {
2222
as: TAs;
23-
} & React.ComponentProps<TAs>,
23+
} & React.ComponentProps<TAs>
2424
) => {
2525
const Comp = props.as as string;
2626

src/08-advanced-patterns/71-as-prop-with-default.problem.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { ElementType } from "react";
22
import { Equal, Expect } from "../helpers/type-utils";
33

4-
export const Link = <TAs extends ElementType>(
4+
export const Link = <TAs extends ElementType = "a">(
55
props: {
6-
as: TAs;
7-
} & React.ComponentPropsWithoutRef<TAs>,
6+
as?: TAs;
7+
} & React.ComponentPropsWithoutRef<TAs>
88
) => {
99
const { as: Comp = "a", ...rest } = props;
1010
return <Comp {...rest}></Comp>;
@@ -66,7 +66,7 @@ const Example2 = () => {
6666

6767
const Custom = (
6868
props: { thisIsRequired: boolean },
69-
ref: React.ForwardedRef<HTMLAnchorElement>,
69+
ref: React.ForwardedRef<HTMLAnchorElement>
7070
) => {
7171
return <a ref={ref} />;
7272
};

0 commit comments

Comments
 (0)