Skip to content

Commit

Permalink
Add test
Browse files Browse the repository at this point in the history
  • Loading branch information
JoviDeCroock committed Nov 30, 2024
1 parent 0d0b859 commit a8832fe
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 10 deletions.
9 changes: 5 additions & 4 deletions hooks/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ export function useReducer(reducer, initialState, init) {
// settles to an equal state we bail the previous one as well. This should
// be tackled all together in a post-render type of hook, that hook should
// also give us the ability to set SKIP_CHILDREN
hookState._updated = initialValue !== hookState._value[0];
hookState._didUpdate = initialValue !== hookState._value[0];
hookState._value = [hookState._value[0], hookState._value[1]];
hookState._didExecute = true;
}
Expand All @@ -219,25 +219,26 @@ options._afterRender = (newVNode, oldVNode) => {
const hooks = newVNode._component.__hooks._list;
const stateHooksThatExecuted = hooks.filter(
/** @type {(x: import('./internal').HookState) => x is import('./internal').ReducerHookState} */
// @ts-expect-error
x => !!x._component && x._didExecute
);

if (
stateHooksThatExecuted.length &&
!stateHooksThatExecuted.some(x => x._updated) &&
!stateHooksThatExecuted.some(x => x._didUpdate) &&
oldVNode.props === newVNode.props
) {
newVNode._component.__hooks._pendingEffects = [];
newVNode._flags |= SKIP_CHILDREN;
}

stateHooksThatExecuted.forEach(hook => {
hook._updated = undefined;
hook._didUpdate = undefined;
hook._didExecute = false;
});
}

if (oldAfterRender) oldAfterRender(newVNode);
if (oldAfterRender) oldAfterRender(newVNode, oldVNode);
};

/**
Expand Down
7 changes: 3 additions & 4 deletions hooks/src/internal.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {
Options as PreactOptions,
Component as PreactComponent,
VNode as PreactVNode,
Context as PreactContext,
Context as PreactContext
} from '../../src/internal';
import { Reducer, StateUpdater } from '.';

Expand Down Expand Up @@ -52,8 +52,6 @@ export type HookState =

interface BaseHookState {
_value?: unknown;
_nextValue?: undefined;
_pendingValue?: undefined;
_args?: undefined;
_pendingArgs?: undefined;
_component?: undefined;
Expand All @@ -72,7 +70,6 @@ export interface EffectHookState extends BaseHookState {

export interface MemoHookState<T = unknown> extends BaseHookState {
_value?: T;
_pendingValue?: T;
_args?: unknown[];
_pendingArgs?: unknown[];
_factory?: () => T;
Expand All @@ -84,6 +81,8 @@ export interface ReducerHookState<S = unknown, A = unknown>
_value?: [S, StateUpdater<S>];
_component?: Component;
_reducer?: Reducer<S, A>;
_didExecute?: boolean;
_didUpdate?: boolean;
}

export interface ContextHookState extends BaseHookState {
Expand Down
99 changes: 98 additions & 1 deletion hooks/test/browser/useState.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import { setupRerender, act } from 'preact/test-utils';
import { createElement, render, createContext, Component } from 'preact';
import { useState, useContext, useEffect } from 'preact/hooks';
import {
useState,
useContext,
useEffect,
useLayoutEffect,
useReducer,
useRef
} from 'preact/hooks';
import { setupScratch, teardown } from '../../../test/_util/helpers';

/** @jsx createElement */
Expand Down Expand Up @@ -419,4 +426,94 @@ describe('useState', () => {
expect(renders).to.equal(2);
});
});

it('Should capture the closure in the reducer', () => {
function createContext2() {
const context = createContext();

const ProviderOrig = context.Provider;
context.Provider = ({ value, children }) => {
const valueRef = useRef(value);
const contextValue = useRef();

if (!contextValue.current) {
contextValue.current = {
value: valueRef,
listener: null
};
}

useLayoutEffect(() => {
valueRef.current = value;
if (contextValue.current.listener) {
contextValue.current.listener([value]);
}
}, [value]);
return (
<ProviderOrig value={contextValue.current}>{children}</ProviderOrig>
);
};

return context;
}

function useContextSelector(context) {
const contextValue = useContext(context);
const {
value: { current: value }
} = contextValue;
const [state, dispatch] = useReducer(
() => {
return {
value
};
},
{
value
}
);
useLayoutEffect(() => {
contextValue.listener = dispatch;
}, []);
return state.value;
}

const context = createContext2();
let set;

function Child() {
const [count, setState] = useContextSelector(context);
const [c, setC] = useState(0);
set = () => {
setC(s => s + 1);
setState(s => s + 1);
};
return (
<div>
<div>Context count: {count}</div>
<div>Local count: {c}</div>
</div>
);
}

// Render this
function App() {
const [state, setState] = useState(0);
return (
<context.Provider value={[state, setState]}>
<Child />
</context.Provider>
);
}

act(() => {
render(<App />, scratch);
});
expect(scratch.textContent).to.equal('Context count: 0Local count: 0');

act(() => {
set();
});
expect(scratch.textContent).to.equal('Context count: 1Local count: 1');
});
});
3 changes: 2 additions & 1 deletion mangle.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@
"$_list": "__",
"$_pendingEffects": "__h",
"$_value": "__",
"$_nextValue": "__N",
"$_didExecute": "__N",
"$_didUpdate": "__U",
"$_original": "__v",
"$_args": "__H",
"$_factory": "__h",
Expand Down

0 comments on commit a8832fe

Please sign in to comment.