Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backport 10.15 release line #4265

Merged
merged 7 commits into from
Jan 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions compat/src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ declare namespace React {
export import Inputs = _hooks.Inputs;
export import PropRef = _hooks.PropRef;
export import Reducer = _hooks.Reducer;
export import Dispatch = _hooks.Dispatch;
export import Ref = _hooks.Ref;
export import StateUpdater = _hooks.StateUpdater;
export import useCallback = _hooks.useCallback;
Expand All @@ -49,6 +50,7 @@ declare namespace React {
): T;

// Preact Defaults
export import Context = preact.Context;
export import ContextType = preact.ContextType;
export import RefObject = preact.RefObject;
export import Component = preact.Component;
Expand Down Expand Up @@ -138,6 +140,10 @@ declare namespace React {
) => boolean
): C;

export interface RefAttributes<R> extends preact.Attributes {
ref?: preact.Ref<R> | undefined;
}

export interface ForwardFn<P = {}, T = any> {
(props: P, ref: ForwardedRef<T>): preact.ComponentChild;
displayName?: string;
Expand All @@ -147,6 +153,11 @@ declare namespace React {
current: T;
}

export interface ForwardRefExoticComponent<P>
extends preact.FunctionComponent<P> {
defaultProps?: Partial<P> | undefined;
}

export type ForwardedRef<T> =
| ((instance: T | null) => void)
| MutableRefObject<T | null>
Expand Down
11 changes: 10 additions & 1 deletion debug/src/debug.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,16 @@ export function initDebug() {
let oldVnode = options.vnode;
let oldCatchError = options._catchError;
let oldRoot = options._root;
let oldRender = options._render;
let oldHook = options._hook;

const warnedComponents = !isWeakMapSupported
? null
: {
useEffect: new WeakMap(),
useLayoutEffect: new WeakMap(),
lazyPropTypes: new WeakMap()
};
};
const deprecations = [];

options._catchError = (error, vnode, oldVNode) => {
Expand Down Expand Up @@ -282,6 +284,13 @@ export function initDebug() {
if (oldBeforeDiff) oldBeforeDiff(internal, vnode);
};

options._render = vnode => {
if (oldRender) {
oldRender(vnode);
}
hooksAllowed = true;
};

options._hook = (internal, index, type) => {
if (!internal || !hooksAllowed) {
throw new Error('Hook can only be invoked from render methods.');
Expand Down
5 changes: 3 additions & 2 deletions hooks/src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export function useState<S = undefined>(): [
];

export type Reducer<S, A> = (prevState: S, action: A) => S;
export type Dispatch<A> = (action: A) => void;
/**
* An alternative to `useState`.
*
Expand All @@ -27,7 +28,7 @@ export type Reducer<S, A> = (prevState: S, action: A) => S;
export function useReducer<S, A>(
reducer: Reducer<S, A>,
initialState: S
): [S, (action: A) => void];
): [S, Dispatch<A>];

/**
* An alternative to `useState`.
Expand All @@ -43,7 +44,7 @@ export function useReducer<S, A, I>(
reducer: Reducer<S, A>,
initialArg: I,
init: (arg: I) => S
): [S, (action: A) => void];
): [S, Dispatch<A>];

/** @deprecated Use the `Ref` type instead. */
type PropRef<T> = MutableRef<T>;
Expand Down
1 change: 1 addition & 0 deletions hooks/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ options._render = internal => {
currentInternal.data.__hooks._pendingEffects.forEach(invokeCleanup);
currentInternal.data.__hooks._pendingEffects.forEach(invokeEffect);
currentInternal.data.__hooks._pendingEffects = [];
currentIndex = 0;
}
}
previousInternal = internal;
Expand Down
47 changes: 46 additions & 1 deletion hooks/test/browser/useEffect.test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { act } from 'preact/test-utils';
import { act, teardown as teardownAct } from 'preact/test-utils';
import { createElement, render, Fragment, Component } from 'preact';
import { useEffect, useState, useRef } from 'preact/hooks';
import { setupScratch, teardown } from '../../../test/_util/helpers';
Expand Down Expand Up @@ -371,6 +371,51 @@ describe('useEffect', () => {
);
});

it('hooks should be called in right order', async () => {
teardownAct();

let increment;

const Counter = () => {
const [count, setCount] = useState(0);
useState('binggo!!');
const renderRoot = useRef();
useEffect(() => {
const div = renderRoot.current;
render(<Dummy />, div);
}, [count]);

increment = () => {
setCount(x => x + 1);
return Promise.resolve().then(() => setCount(x => x + 1));
};

return (
<div>
<div>Count: {count}</div>
<div ref={renderRoot} />
</div>
);
};

const Dummy = () => {
useState();
return <div>dummy</div>;
};

render(<Counter />, scratch);

expect(scratch.innerHTML).to.equal(
'<div><div>Count: 0</div><div></div></div>'
);
/** Using the act function will affect the timing of the useEffect */
await increment();

expect(scratch.innerHTML).to.equal(
'<div><div>Count: 2</div><div><div>dummy</div></div></div>'
);
});

it('handles errors correctly', () => {
class ErrorBoundary extends Component {
constructor(props) {
Expand Down
33 changes: 33 additions & 0 deletions src/jsx.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1072,6 +1072,9 @@ export namespace JSXInternal {
WheelEvent
>;

export type TargetedPictureInPictureEvent<Target extends EventTarget> =
TargetedEvent<Target, PictureInPictureEvent>;

export interface EventHandler<E extends TargetedEvent> {
(this: void, event: E): void;
}
Expand Down Expand Up @@ -1114,6 +1117,8 @@ export namespace JSXInternal {
export type WheelEventHandler<Target extends EventTarget> = EventHandler<
TargetedWheelEvent<Target>
>;
export type PictureInPictureEventHandler<Target extends EventTarget> =
EventHandler<TargetedPictureInPictureEvent<Target>>;

export interface DOMAttributes<Target extends EventTarget>
extends PreactDOMAttributes {
Expand Down Expand Up @@ -1142,6 +1147,10 @@ export namespace JSXInternal {
// Details Events
onToggle?: GenericEventHandler<Target> | undefined;

// Dialog Events
onClose?: GenericEventHandler<Target> | undefined;
onCancel?: GenericEventHandler<Target> | undefined;

// Focus Events
onFocus?: FocusEventHandler<Target> | undefined;
onFocusCapture?: FocusEventHandler<Target> | undefined;
Expand Down Expand Up @@ -1315,8 +1324,22 @@ export namespace JSXInternal {
onAnimationIterationCapture?: AnimationEventHandler<Target> | undefined;

// Transition Events
onTransitionCancel?: TransitionEventHandler<Target>;
onTransitionCancelCapture?: TransitionEventHandler<Target>;
onTransitionEnd?: TransitionEventHandler<Target>;
onTransitionEndCapture?: TransitionEventHandler<Target>;
onTransitionRun?: TransitionEventHandler<Target>;
onTransitionRunCapture?: TransitionEventHandler<Target>;
onTransitionStart?: TransitionEventHandler<Target>;
onTransitionStartCapture?: TransitionEventHandler<Target>;

// PictureInPicture Events
onEnterPictureInPicture?: PictureInPictureEventHandler<Target>;
onEnterPictureInPictureCapture?: PictureInPictureEventHandler<Target>;
onLeavePictureInPicture?: PictureInPictureEventHandler<Target>;
onLeavePictureInPictureCapture?: PictureInPictureEventHandler<Target>;
onResize?: PictureInPictureEventHandler<Target>;
onResizeCapture?: PictureInPictureEventHandler<Target>;
}

// All the WAI-ARIA 1.1 attributes from https://www.w3.org/TR/wai-aria-1.1/
Expand All @@ -1332,6 +1355,16 @@ export namespace JSXInternal {
'aria-autocomplete'?: Signalish<
'none' | 'inline' | 'list' | 'both' | undefined
>;
/**
* Defines a string value that labels the current element, which is intended to be converted into Braille.
* @see aria-label.
*/
'aria-braillelabel'?: Signalish<string | undefined>;
/**
* Defines a human-readable, author-localized abbreviated description for the role of an element, which is intended to be converted into Braille.
* @see aria-roledescription.
*/
'aria-brailleroledescription'?: Signalish<string | undefined>;
/** Indicates an element is being modified and that assistive technologies MAY want to wait until the modifications are complete before exposing them to the user. */
'aria-busy'?: Signalish<Booleanish | undefined>;
/**
Expand Down
Loading