Skip to content

Commit c4ed4f4

Browse files
Move defaultProps into preact/compat (#4657)
* Move `defaultProps` into `preact/compat` This will be handled in `options.vnode` for function/class components. This hook gets called for every invocation of `jsx`/`createElement` and `cloneElement`. * Try it * refactor: This is horrific but seems to work? (#4662) --------- Co-authored-by: Ryan Christian <[email protected]>
1 parent 71f9bef commit c4ed4f4

14 files changed

+245
-239
lines changed

compat/src/index.d.ts

+144-51
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as _hooks from '../../hooks';
22
// Intentionally not using a relative path to take advantage of
33
// the TS version resolution mechanism
4-
import * as preact from 'preact';
4+
import * as preact1 from 'preact';
55
import { JSXInternal } from '../../src/jsx';
66
import * as _Suspense from './suspense';
77

@@ -13,6 +13,99 @@ interface SignalLike<T> {
1313

1414
type Signalish<T> = T | SignalLike<T>;
1515

16+
declare namespace preact {
17+
export interface FunctionComponent<P = {}> {
18+
(
19+
props: preact1.RenderableProps<P>,
20+
context?: any
21+
): preact1.ComponentChildren;
22+
displayName?: string;
23+
defaultProps?: Partial<P> | undefined;
24+
}
25+
26+
export interface ComponentClass<P = {}, S = {}> {
27+
new (props: P, context?: any): preact1.Component<P, S>;
28+
displayName?: string;
29+
defaultProps?: Partial<P>;
30+
contextType?: preact1.Context<any>;
31+
getDerivedStateFromProps?(
32+
props: Readonly<P>,
33+
state: Readonly<S>
34+
): Partial<S> | null;
35+
getDerivedStateFromError?(error: any): Partial<S> | null;
36+
}
37+
38+
export interface Component<P = {}, S = {}> {
39+
componentWillMount?(): void;
40+
componentDidMount?(): void;
41+
componentWillUnmount?(): void;
42+
getChildContext?(): object;
43+
componentWillReceiveProps?(nextProps: Readonly<P>, nextContext: any): void;
44+
shouldComponentUpdate?(
45+
nextProps: Readonly<P>,
46+
nextState: Readonly<S>,
47+
nextContext: any
48+
): boolean;
49+
componentWillUpdate?(
50+
nextProps: Readonly<P>,
51+
nextState: Readonly<S>,
52+
nextContext: any
53+
): void;
54+
getSnapshotBeforeUpdate?(oldProps: Readonly<P>, oldState: Readonly<S>): any;
55+
componentDidUpdate?(
56+
previousProps: Readonly<P>,
57+
previousState: Readonly<S>,
58+
snapshot: any
59+
): void;
60+
componentDidCatch?(error: any, errorInfo: preact1.ErrorInfo): void;
61+
}
62+
63+
export abstract class Component<P, S> {
64+
constructor(props?: P, context?: any);
65+
66+
static displayName?: string;
67+
static defaultProps?: any;
68+
static contextType?: preact1.Context<any>;
69+
70+
// Static members cannot reference class type parameters. This is not
71+
// supported in TypeScript. Reusing the same type arguments from `Component`
72+
// will lead to an impossible state where one cannot satisfy the type
73+
// constraint under no circumstances, see #1356.In general type arguments
74+
// seem to be a bit buggy and not supported well at the time of this
75+
// writing with TS 3.3.3333.
76+
static getDerivedStateFromProps?(
77+
props: Readonly<object>,
78+
state: Readonly<object>
79+
): object | null;
80+
static getDerivedStateFromError?(error: any): object | null;
81+
82+
state: Readonly<S>;
83+
props: preact1.RenderableProps<P>;
84+
context: any;
85+
86+
// From https://github.com/DefinitelyTyped/DefinitelyTyped/blob/e836acc75a78cf0655b5dfdbe81d69fdd4d8a252/types/react/index.d.ts#L402
87+
// // We MUST keep setState() as a unified signature because it allows proper checking of the method return type.
88+
// // See: https://github.com/DefinitelyTyped/DefinitelyTyped/issues/18365#issuecomment-351013257
89+
setState<K extends keyof S>(
90+
state:
91+
| ((
92+
prevState: Readonly<S>,
93+
props: Readonly<P>
94+
) => Pick<S, K> | Partial<S> | null)
95+
| (Pick<S, K> | Partial<S> | null),
96+
callback?: () => void
97+
): void;
98+
99+
forceUpdate(callback?: () => void): void;
100+
101+
abstract render(
102+
props?: preact1.RenderableProps<P>,
103+
state?: Readonly<S>,
104+
context?: any
105+
): preact1.ComponentChildren;
106+
}
107+
}
108+
16109
// export default React;
17110
export = React;
18111
export as namespace React;
@@ -49,32 +142,32 @@ declare namespace React {
49142
): T;
50143

51144
// Preact Defaults
52-
export import Context = preact.Context;
53-
export import ContextType = preact.ContextType;
54-
export import RefObject = preact.RefObject;
145+
export import Context = preact1.Context;
146+
export import ContextType = preact1.ContextType;
147+
export import RefObject = preact1.RefObject;
55148
export import Component = preact.Component;
56149
export import FunctionComponent = preact.FunctionComponent;
57-
export import ComponentType = preact.ComponentType;
150+
export import ComponentType = preact1.ComponentType;
58151
export import ComponentClass = preact.ComponentClass;
59-
export import FC = preact.FunctionComponent;
60-
export import createContext = preact.createContext;
61-
export import Ref = preact.Ref;
62-
export import createRef = preact.createRef;
63-
export import Fragment = preact.Fragment;
64-
export import createElement = preact.createElement;
65-
export import cloneElement = preact.cloneElement;
66-
export import ComponentProps = preact.ComponentProps;
67-
export import ReactNode = preact.ComponentChild;
68-
export import ReactElement = preact.VNode;
69-
export import Consumer = preact.Consumer;
70-
export import ErrorInfo = preact.ErrorInfo;
152+
export import FC = preact1.FunctionComponent;
153+
export import createContext = preact1.createContext;
154+
export import Ref = preact1.Ref;
155+
export import createRef = preact1.createRef;
156+
export import Fragment = preact1.Fragment;
157+
export import createElement = preact1.createElement;
158+
export import cloneElement = preact1.cloneElement;
159+
export import ComponentProps = preact1.ComponentProps;
160+
export import ReactNode = preact1.ComponentChild;
161+
export import ReactElement = preact1.VNode;
162+
export import Consumer = preact1.Consumer;
163+
export import ErrorInfo = preact1.ErrorInfo;
71164

72165
// Suspense
73166
export import Suspense = _Suspense.Suspense;
74167
export import lazy = _Suspense.lazy;
75168

76169
// Compat
77-
export import StrictMode = preact.Fragment;
170+
export import StrictMode = preact1.Fragment;
78171
export const version: string;
79172
export function startTransition(cb: () => void): void;
80173

@@ -83,15 +176,15 @@ declare namespace React {
83176
extends JSXInternal.HTMLAttributes<T> {}
84177
export interface HTMLProps<T extends EventTarget>
85178
extends JSXInternal.AllHTMLAttributes<T>,
86-
preact.ClassAttributes<T> {}
179+
preact1.ClassAttributes<T> {}
87180
export interface AllHTMLAttributes<T extends EventTarget>
88181
extends JSXInternal.AllHTMLAttributes<T> {}
89182
export import DetailedHTMLProps = JSXInternal.DetailedHTMLProps;
90183
export import CSSProperties = JSXInternal.CSSProperties;
91184

92185
export interface SVGProps<T extends EventTarget>
93186
extends JSXInternal.SVGAttributes<T>,
94-
preact.ClassAttributes<T> {}
187+
preact1.ClassAttributes<T> {}
95188

96189
interface SVGAttributes extends JSXInternal.SVGAttributes {}
97190

@@ -186,81 +279,81 @@ declare namespace React {
186279
export import TransitionEventHandler = JSXInternal.TransitionEventHandler;
187280

188281
export function createPortal(
189-
vnode: preact.ComponentChildren,
190-
container: preact.ContainerNode
191-
): preact.VNode<any>;
282+
vnode: preact1.ComponentChildren,
283+
container: preact1.ContainerNode
284+
): preact1.VNode<any>;
192285

193286
export function render(
194-
vnode: preact.ComponentChild,
195-
parent: preact.ContainerNode,
287+
vnode: preact1.ComponentChild,
288+
parent: preact1.ContainerNode,
196289
callback?: () => void
197290
): Component | null;
198291

199292
export function hydrate(
200-
vnode: preact.ComponentChild,
201-
parent: preact.ContainerNode,
293+
vnode: preact1.ComponentChild,
294+
parent: preact1.ContainerNode,
202295
callback?: () => void
203296
): Component | null;
204297

205298
export function unmountComponentAtNode(
206-
container: preact.ContainerNode
299+
container: preact1.ContainerNode
207300
): boolean;
208301

209302
export function createFactory(
210-
type: preact.VNode<any>['type']
303+
type: preact1.VNode<any>['type']
211304
): (
212305
props?: any,
213-
...children: preact.ComponentChildren[]
214-
) => preact.VNode<any>;
306+
...children: preact1.ComponentChildren[]
307+
) => preact1.VNode<any>;
215308
export function isValidElement(element: any): boolean;
216309
export function isFragment(element: any): boolean;
217310
export function isMemo(element: any): boolean;
218311
export function findDOMNode(
219-
component: preact.Component | Element
312+
component: preact1.Component | Element
220313
): Element | null;
221314

222315
export abstract class PureComponent<
223316
P = {},
224317
S = {},
225318
SS = any
226-
> extends preact.Component<P, S> {
319+
> extends preact1.Component<P, S> {
227320
isPureReactComponent: boolean;
228321
}
229322

230-
export type MemoExoticComponent<C extends preact.FunctionalComponent<any>> =
231-
preact.FunctionComponent<ComponentProps<C>> & {
323+
export type MemoExoticComponent<C extends preact1.FunctionalComponent<any>> =
324+
preact1.FunctionComponent<ComponentProps<C>> & {
232325
readonly type: C;
233326
};
234327

235328
export function memo<P = {}>(
236-
component: preact.FunctionalComponent<P>,
329+
component: preact1.FunctionalComponent<P>,
237330
comparer?: (prev: P, next: P) => boolean
238-
): preact.FunctionComponent<P>;
239-
export function memo<C extends preact.FunctionalComponent<any>>(
331+
): preact1.FunctionComponent<P>;
332+
export function memo<C extends preact1.FunctionalComponent<any>>(
240333
component: C,
241334
comparer?: (
242-
prev: preact.ComponentProps<C>,
243-
next: preact.ComponentProps<C>
335+
prev: preact1.ComponentProps<C>,
336+
next: preact1.ComponentProps<C>
244337
) => boolean
245338
): C;
246339

247-
export interface RefAttributes<R> extends preact.Attributes {
248-
ref?: preact.Ref<R> | undefined;
340+
export interface RefAttributes<R> extends preact1.Attributes {
341+
ref?: preact1.Ref<R> | undefined;
249342
}
250343

251344
export interface ForwardFn<P = {}, T = any> {
252-
(props: P, ref: ForwardedRef<T>): preact.ComponentChild;
345+
(props: P, ref: ForwardedRef<T>): preact1.ComponentChild;
253346
displayName?: string;
254347
}
255348

256349
export interface ForwardRefExoticComponent<P>
257-
extends preact.FunctionComponent<P> {
350+
extends preact1.FunctionComponent<P> {
258351
defaultProps?: Partial<P> | undefined;
259352
}
260353

261354
export function forwardRef<R, P = {}>(
262355
fn: ForwardFn<P, R>
263-
): preact.FunctionalComponent<PropsWithoutRef<P> & { ref?: preact.Ref<R> }>;
356+
): preact1.FunctionalComponent<PropsWithoutRef<P> & { ref?: preact1.Ref<R> }>;
264357

265358
export type PropsWithoutRef<P> = Omit<P, 'ref'>;
266359

@@ -308,21 +401,21 @@ declare namespace React {
308401
export function flushSync<A, R>(fn: (a: A) => R, a: A): R;
309402

310403
export type PropsWithChildren<P = unknown> = P & {
311-
children?: preact.ComponentChildren | undefined;
404+
children?: preact1.ComponentChildren | undefined;
312405
};
313406

314407
export const Children: {
315-
map<T extends preact.ComponentChild, R>(
408+
map<T extends preact1.ComponentChild, R>(
316409
children: T | T[],
317410
fn: (child: T, i: number) => R
318411
): R[];
319-
forEach<T extends preact.ComponentChild>(
412+
forEach<T extends preact1.ComponentChild>(
320413
children: T | T[],
321414
fn: (child: T, i: number) => void
322415
): void;
323-
count: (children: preact.ComponentChildren) => number;
324-
only: (children: preact.ComponentChildren) => preact.ComponentChild;
325-
toArray: (children: preact.ComponentChildren) => preact.VNode<{}>[];
416+
count: (children: preact1.ComponentChildren) => number;
417+
only: (children: preact1.ComponentChildren) => preact1.ComponentChild;
418+
toArray: (children: preact1.ComponentChildren) => preact1.VNode<{}>[];
326419
};
327420

328421
// scheduler

compat/src/render.js

+9-1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
useSyncExternalStore,
2525
useTransition
2626
} from './index';
27+
import { assign } from './util';
2728

2829
export const REACT_ELEMENT_TYPE = Symbol.for('react.element');
2930

@@ -237,8 +238,15 @@ options.vnode = vnode => {
237238
// only normalize props on Element nodes
238239
if (typeof vnode.type === 'string') {
239240
handleDomVNode(vnode);
241+
} else if (typeof vnode.type === 'function' && vnode.type.defaultProps) {
242+
let normalizedProps = assign({}, vnode.props);
243+
for (let i in vnode.type.defaultProps) {
244+
if (normalizedProps[i] === undefined) {
245+
normalizedProps[i] = vnode.type.defaultProps[i];
246+
}
247+
}
248+
vnode.props = normalizedProps;
240249
}
241-
242250
vnode.$$typeof = REACT_ELEMENT_TYPE;
243251

244252
if (oldVNodeHook) oldVNodeHook(vnode);

0 commit comments

Comments
 (0)