Skip to content

Commit

Permalink
Merge pull request #2883 from preactjs/improve-jsdoc
Browse files Browse the repository at this point in the history
Improve JSDoc comments
  • Loading branch information
marvinhagemeister authored Dec 25, 2020
2 parents e8a4c46 + 95d60a2 commit 37a884c
Show file tree
Hide file tree
Showing 14 changed files with 116 additions and 44 deletions.
2 changes: 1 addition & 1 deletion compat/src/forwardRef.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const REACT_FORWARD_SYMBOL =
* wrap components. Using `forwardRef` there is an easy way to get a reference
* of the wrapped component instead of one of the wrapper itself.
* @param {import('./index').ForwardFn} fn
* @returns {import('./internal').FunctionalComponent}
* @returns {import('./internal').FunctionComponent}
*/
export function forwardRef(fn) {
// We always have ref in props.ref, except for
Expand Down
5 changes: 2 additions & 3 deletions compat/src/internal.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {
Component as PreactComponent,
VNode as PreactVNode,
FunctionalComponent as PreactFunctionalComponent
FunctionComponent as PreactFunctionComponent
} from '../../src/internal';
import { SuspenseProps } from './suspense';

Expand All @@ -19,8 +19,7 @@ export interface Component<P = {}, S = {}> extends PreactComponent<P, S> {
_suspendedComponentWillUnmount?(): void;
}

export interface FunctionalComponent<P = {}>
extends PreactFunctionalComponent<P> {
export interface FunctionComponent<P = {}> extends PreactFunctionComponent<P> {
shouldComponentUpdate?(nextProps: Readonly<P>): boolean;
_forwarded?: boolean;
_patchedLifecycles?: true;
Expand Down
4 changes: 2 additions & 2 deletions compat/src/memo.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { shallowDiffers } from './util';
/**
* Memoize a component, so that it only updates when the props actually have
* changed. This was previously known as `React.pure`.
* @param {import('./internal').FunctionalComponent} c functional component
* @param {import('./internal').FunctionComponent} c functional component
* @param {(prev: object, next: object) => boolean} [comparer] Custom equality function
* @returns {import('./internal').FunctionalComponent}
* @returns {import('./internal').FunctionComponent}
*/
export function memo(c, comparer) {
function shouldUpdate(nextProps) {
Expand Down
9 changes: 7 additions & 2 deletions hooks/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ options.unmount = vnode => {
* Get a hook's state from the currentComponent
* @param {number} index The index of the hook to get
* @param {number} type The index of the hook to get
* @returns {import('./internal').HookState}
* @returns {any}
*/
function getHookState(index, type) {
if (options._hook) {
Expand All @@ -120,7 +120,7 @@ function getHookState(index, type) {
}

/**
* @param {import('./index').StateUpdater<any>} initialState
* @param {import('./index').StateUpdater<any>} [initialState]
*/
export function useState(initialState) {
currentHook = 1;
Expand Down Expand Up @@ -240,6 +240,7 @@ export function useContext(context) {
// We could skip this call here, but than we'd not call
// `options._hook`. We need to do that in order to make
// the devtools aware of this hook.
/** @type {import('./internal').ContextHookState} */
const state = getHookState(currentIndex++, 9);
// The devtools needs access to the context object to
// be able to pull of the default value when no provider
Expand All @@ -264,7 +265,11 @@ export function useDebugValue(value, formatter) {
}
}

/**
* @param {(error: any) => void} cb
*/
export function useErrorBoundary(cb) {
/** @type {import('./internal').ErrorBoundaryHookState} */
const state = getHookState(currentIndex++, 10);
const errState = useState();
state._value = cb;
Expand Down
26 changes: 22 additions & 4 deletions hooks/src/internal.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { Component as PreactComponent } from '../../src/internal';
import {
Component as PreactComponent,
PreactContext
} from '../../src/internal';
import { Reducer } from '.';

export { PreactContext } from '../../src/internal';
export { PreactContext };

/**
* The type of arguments passed to a Hook function. While this type is not
Expand Down Expand Up @@ -33,15 +36,20 @@ export interface Component extends PreactComponent<any, any> {
__hooks?: ComponentHooks;
}

export type HookState = EffectHookState | MemoHookState | ReducerHookState;
export type HookState =
| EffectHookState
| MemoHookState
| ReducerHookState
| ContextHookState
| ErrorBoundaryHookState;

export type Effect = () => void | Cleanup;
export type Cleanup = () => void;

export interface EffectHookState {
_value?: Effect;
_args?: any[];
_cleanup?: Cleanup;
_cleanup?: Cleanup | void;
}

export interface MemoHookState {
Expand All @@ -55,3 +63,13 @@ export interface ReducerHookState {
_component?: Component;
_reducer?: Reducer<any, any>;
}

export interface ContextHookState {
/** Whether this hooks as subscribed to updates yet */
_value?: boolean;
_context?: PreactContext;
}

export interface ErrorBoundaryHookState {
_value?: (error: any) => void;
}
2 changes: 1 addition & 1 deletion src/clone-element.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { createVNode } from './create-element';
* Clones the given VNode, optionally adding attributes/props and replacing its children.
* @param {import('./internal').VNode} vnode The virtual DOM element to clone
* @param {object} props Attributes/props to add when cloning
* @param {Array<import('./index').ComponentChildren>} rest Any additional arguments will be used as replacement children.
* @param {Array<import('./internal').ComponentChildren>} rest Any additional arguments will be used as replacement children.
* @returns {import('./internal').VNode}
*/
export function cloneElement(vnode, props, children) {
Expand Down
2 changes: 2 additions & 0 deletions src/component.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export function Component(props, context) {

/**
* Update component state and schedule a re-render.
* @this {import('./internal').Component}
* @param {object | ((s: object, p: object) => object)} update A hash of state
* properties to update with new values or a function that given the current
* state and props returns a new partial state
Expand Down Expand Up @@ -53,6 +54,7 @@ Component.prototype.setState = function(update, callback) {

/**
* Immediately perform a synchronous re-render of the component
* @this {import('./internal').Component}
* @param {() => void} [callback] A function to be called after component is
* re-rendered
*/
Expand Down
8 changes: 5 additions & 3 deletions src/create-context.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,18 @@ export function createContext(defaultValue, contextId) {
const context = {
_id: contextId,
_defaultValue: defaultValue,
/** @type {import('./internal').FunctionComponent} */
Consumer(props, contextValue) {
// return props.children(
// context[contextId] ? context[contextId].props.value : defaultValue
// );
return props.children(contextValue);
},
Provider(props, subs, ctx) {
/** @type {import('./internal').FunctionComponent} */
Provider(props) {
if (!this.getChildContext) {
subs = [];
ctx = {};
let subs = [];
let ctx = {};
ctx[contextId] = this;

this.getChildContext = () => ctx;
Expand Down
6 changes: 4 additions & 2 deletions src/diff/children.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { getDomSibling } from '../component';
* Diff the children of a virtual node
* @param {import('../internal').PreactElement} parentDom The DOM element whose
* children are being diffed
* @param {import('../index').ComponentChildren[]} renderResult
* @param {import('../internal').ComponentChildren[]} renderResult
* @param {import('../internal').VNode} newParentVNode The new virtual
* node whose children should be diff'ed against oldParentVNode
* @param {import('../internal').VNode} oldParentVNode The old virtual
Expand All @@ -18,7 +18,7 @@ import { getDomSibling } from '../component';
* @param {Array<import('../internal').PreactElement>} excessDomChildren
* @param {Array<import('../internal').Component>} commitQueue List of components
* which have callbacks to invoke in commitRoot
* @param {Node | Text} oldDom The current attached DOM
* @param {import('../internal').PreactElement} oldDom The current attached DOM
* element any new dom elements should be placed around. Likely `null` on first
* render (except when hydrating). Can be a sibling DOM element when diffing
* Fragments that have siblings. In most cases, it starts out as `oldChildren[0]._dom`.
Expand Down Expand Up @@ -201,6 +201,8 @@ export function diffChildren(
// To fix it we make sure to reset the inferred value, so that our own
// value check in `diff()` won't be skipped.
if (!isHydrating && newParentVNode.type === 'option') {
// @ts-ignore We have validated that the type of parentDOM is 'option'
// in the above check
parentDom.value = '';
} else if (typeof newParentVNode.type == 'function') {
// Because the newParentVNode is Fragment-like, we need to set it's
Expand Down
27 changes: 20 additions & 7 deletions src/diff/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import options from '../options';
* @param {Array<import('../internal').PreactElement>} excessDomChildren
* @param {Array<import('../internal').Component>} commitQueue List of components
* which have callbacks to invoke in commitRoot
* @param {Element | Text} oldDom The current attached DOM
* @param {import('../internal').PreactElement} oldDom The current attached DOM
* element any new dom elements should be placed around. Likely `null` on first
* render (except when hydrating). Can be a sibling DOM element when diffing
* Fragments that have siblings. In most cases, it starts out as `oldChildren[0]._dom`.
Expand Down Expand Up @@ -73,8 +73,10 @@ export function diff(
} else {
// Instantiate the new component
if ('prototype' in newType && newType.prototype.render) {
// @ts-ignore The check above verifies that newType is suppose to be constructed
newVNode._component = c = new newType(newProps, componentContext); // eslint-disable-line new-cap
} else {
// @ts-ignore Trust me, Component implements the interface we want
newVNode._component = c = new Component(newProps, componentContext);
c.constructor = newType;
c.render = doRender;
Expand Down Expand Up @@ -261,9 +263,11 @@ export function commitRoot(commitQueue, root) {

commitQueue.some(c => {
try {
// @ts-ignore Reuse the commitQueue variable here so the type changes
commitQueue = c._renderCallbacks;
c._renderCallbacks = [];
commitQueue.some(cb => {
// @ts-ignore See above ts-ignore on commitQueue
cb.call(c);
});
} catch (e) {
Expand Down Expand Up @@ -326,15 +330,24 @@ function diffElementNodes(

if (dom == null) {
if (newVNode.type === null) {
// @ts-ignore createTextNode returns Text, we expect PreactElement
return document.createTextNode(newProps);
}

dom = isSvg
? document.createElementNS('http://www.w3.org/2000/svg', newVNode.type)
: document.createElement(
newVNode.type,
newProps.is && { is: newProps.is }
);
if (isSvg) {
dom = document.createElementNS(
'http://www.w3.org/2000/svg',
// @ts-ignore We know `newVNode.type` is a string
newVNode.type
);
} else {
dom = document.createElement(
// @ts-ignore We know `newVNode.type` is a string
newVNode.type,
newProps.is && { is: newProps.is }
);
}

// we created a new parent, so none of the previously attached children can be reused:
excessDomChildren = null;
// we are creating a new node, so we can assume this is a new subtree (in case we are hydrating), this deopts the hydrate
Expand Down
1 change: 1 addition & 0 deletions src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ declare namespace preact {
new (props: P, context?: any): Component<P, S>;
displayName?: string;
defaultProps?: Partial<P>;
contextType?: Context<any>;
getDerivedStateFromProps?(
props: Readonly<P>,
state: Readonly<S>
Expand Down
61 changes: 45 additions & 16 deletions src/internal.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export interface Options extends preact.Options {
_vnodeId: number;
/** Attach a hook that is invoked before render, mainly to check the arguments. */
_root?(
vnode: preact.ComponentChild,
vnode: ComponentChild,
parent: Element | Document | ShadowRoot | DocumentFragment
): void;
/** Attach a hook that is invoked before a vnode is diffed. */
Expand All @@ -38,25 +38,47 @@ export interface Options extends preact.Options {
/** Bypass effect execution. Currenty only used in devtools for hooks inspection */
_skipEffects?: boolean;
/** Attach a hook that is invoked after an error is caught in a component but before calling lifecycle hooks */
_catchError(error: any, vnode: VNode, oldVNode: VNode | undefined): void;
_catchError(error: any, vnode: VNode, oldVNode?: VNode | undefined): void;
}

export interface FunctionalComponent<P = {}>
extends preact.FunctionComponent<P> {
// Define getDerivedStateFromProps as undefined on FunctionalComponent
// to get rid of some errors in `diff()`
export type ComponentChild =
| VNode<any>
| string
| number
| boolean
| null
| undefined;
export type ComponentChildren = ComponentChild[] | ComponentChild;

export interface FunctionComponent<P = {}> extends preact.FunctionComponent<P> {
// Internally, createContext uses `contextType` on a Function component to
// implement the Consumer component
contextType?: PreactContext;

// Internally, createContext stores a ref to the context object on the Provider
// Function component to help devtools
_contextRef?: PreactContext;

// Define these properties as undefined on FunctionComponent to get rid of
// some errors in `diff()`
getDerivedStateFromProps?: undefined;
getDerivedStateFromError?: undefined;
}

// Redefine ComponentFactory using our new internal FunctionalComponent interface above
export type ComponentFactory<P> =
| preact.ComponentClass<P>
| FunctionalComponent<P>;
export interface ComponentClass<P = {}> extends preact.ComponentClass<P> {
_contextRef?: any;

export interface PreactElement extends HTMLElement, Text {
// Override public contextType with internal PreactContext type
contextType?: PreactContext;
}

// Redefine ComponentType using our new internal FunctionComponent interface above
export type ComponentType<P = {}> = ComponentClass<P> | FunctionComponent<P>;

export interface PreactElement extends HTMLElement {
_children?: VNode<any> | null;
/** Event listeners to support event delegation */
_listeners: Record<string, (e: Event) => void>;
_listeners?: Record<string, (e: Event) => void>;

// Preact uses this attribute to detect SVG nodes
ownerSVGElement?: SVGElement | null;
Expand All @@ -66,10 +88,17 @@ export interface PreactElement extends HTMLElement, Text {
data?: string | number; // From Text node
}

// We use the `current` property to differentiate between the two kinds of Refs so
// internally we'll define `current` on both to make TypeScript happy
type RefObject<T> = { current: T | null };
type RefCallback<T> = { (instance: T | null): void; current: undefined };
type Ref<T> = RefObject<T> | RefCallback<T>;

export interface VNode<P = {}> extends preact.VNode<P> {
// Redefine type here using our internal ComponentFactory type
type: string | ComponentFactory<P>;
props: P & { children: preact.ComponentChildren };
// Redefine type here using our internal ComponentType type
type: string | ComponentType<P>;
props: P & { children: ComponentChildren };
ref?: Ref<any> | null;
_children: Array<VNode<any>> | null;
_parent: VNode | null;
_depth: number | null;
Expand All @@ -89,7 +118,7 @@ export interface VNode<P = {}> extends preact.VNode<P> {

export interface Component<P = {}, S = {}> extends preact.Component<P, S> {
// When component is functional component, this is reset to functional component
constructor: preact.ComponentType<P>;
constructor: ComponentType<P>;
state: S; // Override Component["state"] to not be readonly for internal use, specifically Hooks
base?: PreactElement;

Expand Down
6 changes: 3 additions & 3 deletions src/render.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ const IS_HYDRATE = EMPTY_OBJ;

/**
* Render a Preact virtual node into a DOM element
* @param {import('./index').ComponentChild} vnode The virtual node to render
* @param {import('./internal').ComponentChild} vnode The virtual node to render
* @param {import('./internal').PreactElement} parentDom The DOM element to
* render into
* @param {Element | Text} [replaceNode] Optional: Attempt to re-use an
* @param {import('./internal').PreactElement | object} [replaceNode] Optional: Attempt to re-use an
* existing DOM tree rooted at `replaceNode`
*/
export function render(vnode, parentDom, replaceNode) {
Expand Down Expand Up @@ -59,7 +59,7 @@ export function render(vnode, parentDom, replaceNode) {

/**
* Update an existing DOM element with data from a Preact virtual node
* @param {import('./index').ComponentChild} vnode The virtual node to render
* @param {import('./internal').ComponentChild} vnode The virtual node to render
* @param {import('./internal').PreactElement} parentDom The DOM element to
* update
*/
Expand Down
Loading

0 comments on commit 37a884c

Please sign in to comment.