Skip to content

Commit

Permalink
Merge branch 'master' into fix/skip-setter-for-href
Browse files Browse the repository at this point in the history
  • Loading branch information
cristianbote committed Oct 21, 2019
2 parents c687b41 + 013dc38 commit 75b2f0d
Show file tree
Hide file tree
Showing 30 changed files with 373 additions and 152 deletions.
1 change: 1 addition & 0 deletions compat/mangle.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"$_processingException": "__p",
"$_root": "__p",
"$_commit": "__c",
"$_suspensions": "__u",
"$_diff": "__b",
"$_render": "__r",
"$_hook": "__h",
Expand Down
9 changes: 6 additions & 3 deletions compat/src/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import * as _hooks from '../../hooks';
import * as preact from '../../src';
import { JSXInternal } from '../../src/jsx'
import * as _internal from './internal';
import * as _Suspense from './suspense';

// export default React;
Expand Down Expand Up @@ -43,7 +42,6 @@ declare namespace React {
export import lazy = _Suspense.lazy;

// Compat
export import ForwardFn = _internal.ForwardFn;
export const version: string;

export function createPortal(vnode: preact.VNode, container: Element): preact.VNode<any>;
Expand All @@ -64,7 +62,12 @@ declare namespace React {

export function memo<P = {}>(component: preact.FunctionalComponent<P>, comparer?: (prev: P, next: P) => boolean): preact.FunctionComponent<P>;

export function forwardRef<R, P = {}>(fn: _internal.ForwardFn<P, R>): preact.FunctionalComponent<P>;
export interface ForwardFn<P = {}, T = any> {
(props: P, ref: Ref<T>): preact.ComponentChild;
displayName?: string;
}

export function forwardRef<R, P = {}>(fn: ForwardFn<P, R>): preact.FunctionalComponent<P>;

export function unstable_batchedUpdates(callback: (arg?: any) => void, arg?: any): void;

Expand Down
6 changes: 0 additions & 6 deletions compat/src/internal.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { Ref, ComponentChild } from '../..';
import {
Component as PreactComponent,
VNode as PreactVNode,
Expand Down Expand Up @@ -27,11 +26,6 @@ export interface VNode<T = any> extends PreactVNode<T> {
preactCompatNormalized?: boolean;
}

export interface ForwardFn<P = {}, T = any> {
(props: P, ref: Ref<T>): ComponentChild;
displayName?: string;
}

export interface SuspenseState {
_parkedChildren: VNode<any>[];
}
Expand Down
12 changes: 12 additions & 0 deletions compat/test/browser/forwardRef.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -296,4 +296,16 @@ describe('forwardRef', () => {
expect(ref.current==null).to.equal(true);
expect(differentRef.current.nodeName).to.equal('DIV');
});

it('calls ref when this is a function.', () => {
const spy = sinon.spy();
const Bar = forwardRef((props, ref) => {
useImperativeHandle(ref, () => ({ foo: 100 }));
return null;
});

render(<Bar ref={spy} />, scratch);
expect(spy).to.be.calledOnce;
expect(spy).to.be.calledWithExactly({ foo: 100 });
});
});
2 changes: 1 addition & 1 deletion compat/test/ts/forward-ref.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import * as React from '../../src';
import React from '../../src';

const MyInput: React.ForwardFn<{ id: string }, { focus(): void }> = (props, ref) => {
const inputRef = React.useRef<HTMLInputElement>()
Expand Down
1 change: 1 addition & 0 deletions debug/mangle.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"$_prevState": "__u",
"$_nextState": "__s",
"$_renderCallbacks": "__h",
"$_suspensions": "__u",
"$_vnode": "__v",
"$_children": "__k",
"$_dom": "__e",
Expand Down
4 changes: 2 additions & 2 deletions debug/src/devtools/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,12 @@ export function initDevTools() {
bundleType: /* istanbul ignore next */ isDev ? 1 : 0,
version: '16.5.2',
rendererPackageName: 'preact',
// We don't need this, but the devtools `attachRenderer` function relys
// We don't need this, but the devtools `attachRenderer` function relies on
// it being there.
findHostInstanceByFiber(vnode) {
return vnode._dom;
},
// We don't need this, but the devtools `attachRenderer` function relys
// We don't need this, but the devtools `attachRenderer` function relies on
// it being there.
findFiberByHostInstance(instance) {
return preactRenderer.inst2vnode.get(instance) || null;
Expand Down
2 changes: 1 addition & 1 deletion debug/test/browser/debug.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -552,7 +552,7 @@ describe('debug', () => {
expect(console.error).to.not.be.called;
});

it('Accepts minimial well formed table', () => {
it('Accepts minimal well formed table', () => {
const Table = () => (
<table>
<tr>
Expand Down
2 changes: 1 addition & 1 deletion demo/reduxUpdate.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class Test extends Component {
store.dispatch({ type: 1, display });
}}
>
Toggle visibilty
Toggle visibility
</button>
<Link to={`/${(parseInt(this.props.start) || 0) + 1}`}>Click me</Link>

Expand Down
2 changes: 1 addition & 1 deletion demo/spiral.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export default class Spiral extends Component {
}

componentWillUnmount() {
console.log('unmout');
console.log('unmount');
this.stop = true;
}

Expand Down
1 change: 1 addition & 0 deletions hooks/mangle.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"$_diff": "__b",
"$_commit": "__c",
"$_render": "__r",
"$_suspensions": "__u",
"$_hook": "__h",
"$_catchError": "__e",
"$_unmount": "_e"
Expand Down
62 changes: 42 additions & 20 deletions hooks/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ options.diffed = vnode => {

const hooks = c.__hooks;
if (hooks) {
hooks._handles = bindHandles(hooks._handles);
hooks._pendingLayoutEffects = handleEffects(hooks._pendingLayoutEffects);
}
};
Expand Down Expand Up @@ -61,18 +60,27 @@ function getHookState(index) {
// * https://github.com/michael-klein/funcy.js/blob/650beaa58c43c33a74820a3c98b3c7079cf2e333/src/renderer.mjs
// Other implementations to look at:
// * https://codesandbox.io/s/mnox05qp8
const hooks = currentComponent.__hooks || (currentComponent.__hooks = { _list: [], _pendingEffects: [], _pendingLayoutEffects: [], _handles: [] });
const hooks = currentComponent.__hooks || (currentComponent.__hooks = { _list: [], _pendingEffects: [], _pendingLayoutEffects: [] });

if (index >= hooks._list.length) {
hooks._list.push({});
}
return hooks._list[index];
}

/**
* @param {import('./index').StateUpdater<any>} initialState
*/
export function useState(initialState) {
return useReducer(invokeOrReturn, initialState);
}

/**
* @param {import('./index').Reducer<any, any>} reducer
* @param {import('./index').StateUpdater<any>} initialState
* @param {(initialState: any) => void} [init]
* @returns {[ any, (state: any) => void ]}
*/
export function useReducer(reducer, initialState, init) {

/** @type {import('./internal').ReducerHookState} */
Expand Down Expand Up @@ -132,19 +140,16 @@ export function useRef(initialValue) {
return useMemo(() => ({ current: initialValue }), []);
}

/**
* @param {object} ref
* @param {() => object} createHandle
* @param {any[]} args
*/
export function useImperativeHandle(ref, createHandle, args) {
const state = getHookState(currentIndex++);
if (argsChanged(state._args, args)) {
state._args = args;
currentComponent.__hooks._handles.push({ ref, createHandle });
}
}

function bindHandles(handles) {
handles.some(handle => {
if (handle.ref) handle.ref.current = handle.createHandle();
});
return [];
useLayoutEffect(() => {
if (typeof ref === 'function') ref(createHandle());
else if (ref) ref.current = createHandle();
}, args == null ? args : args.concat(ref));
}

/**
Expand Down Expand Up @@ -212,7 +217,7 @@ let afterPaint = () => {};
*/
function flushAfterPaintEffects() {
afterPaintEffects.some(component => {
component._afterPaintQueued = false;
component._afterPaintQueued = 0;
if (component._parentDom) {
component.__hooks._pendingEffects = handleEffects(component.__hooks._pendingEffects);
}
Expand All @@ -223,9 +228,14 @@ function flushAfterPaintEffects() {
const RAF_TIMEOUT = 100;

/**
* requestAnimationFrame with a timeout in case it doesn't fire (for example if the browser tab is not visible)
* Schedule a callback to be invoked after the browser has a chance to paint a new frame.
* Do this by combining requestAnimationFrame (rAF) + setTimeout to invoke a callback after
* the next browser frame.
*
* Also, schedule a timeout in parallel to the the rAF to ensure the callback is invoked
* even if RAF doesn't fire (for example if the browser tab is not visible)
*/
function safeRaf(callback) {
function afterNextFrame(callback) {
const done = () => {
clearTimeout(timeout);
cancelAnimationFrame(raf);
Expand All @@ -239,22 +249,30 @@ function safeRaf(callback) {
if (typeof window !== 'undefined') {
let prevRaf = options.requestAnimationFrame;
afterPaint = (component) => {
if ((!component._afterPaintQueued && (component._afterPaintQueued = true) && afterPaintEffects.push(component) === 1) ||
prevRaf !== options.requestAnimationFrame) {
if (
(!component._afterPaintQueued && (component._afterPaintQueued = afterPaintEffects.push(component)) === 1)
|| prevRaf !== options.requestAnimationFrame
) {
prevRaf = options.requestAnimationFrame;

/* istanbul ignore next */
(options.requestAnimationFrame || safeRaf)(flushAfterPaintEffects);
(prevRaf || afterNextFrame)(flushAfterPaintEffects);
}
};
}

/**
* @param {import('./internal').EffectHookState[]} effects
*/
function handleEffects(effects) {
effects.forEach(invokeCleanup);
effects.forEach(invokeEffect);
return [];
}

/**
* @param {import('./internal').EffectHookState} hook
*/
function invokeCleanup(hook) {
if (hook._cleanup) hook._cleanup();
}
Expand All @@ -268,6 +286,10 @@ function invokeEffect(hook) {
if (typeof result === 'function') hook._cleanup = result;
}

/**
* @param {any[]} oldArgs
* @param {any[]} newArgs
*/
function argsChanged(oldArgs, newArgs) {
return !oldArgs || newArgs.some((arg, index) => arg !== oldArgs[index]);
}
Expand Down
22 changes: 22 additions & 0 deletions hooks/test/browser/useContext.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,28 @@ describe('useContext', () => {
expect(unmountspy).not.to.be.called;
});

it('should only subscribe a component once', () => {
const values = [];
const Context = createContext(13);
let provider, subSpy;

function Comp() {
const value = useContext(Context);
values.push(value);
return null;
}

render(<Comp />, scratch);

render(<Context.Provider ref={p => provider = p} value={42}><Comp /></Context.Provider>, scratch);
subSpy = sinon.spy(provider, 'sub');

render(<Context.Provider value={69}><Comp /></Context.Provider>, scratch);
expect(subSpy).to.not.have.been.called;

expect(values).to.deep.equal([13, 42, 69]);
});

it('should maintain context', done => {
const context = createContext(null);
const { Provider } = context;
Expand Down
Loading

0 comments on commit 75b2f0d

Please sign in to comment.