diff --git a/hooks/src/index.js b/hooks/src/index.js index 094410d7a3..e70353ba05 100644 --- a/hooks/src/index.js +++ b/hooks/src/index.js @@ -15,8 +15,6 @@ let currentHook = 0; /** @type {Array} */ let afterPaintEffects = []; -let EMPTY = []; - // Cast to use internal Options type const options = /** @type {import('./internal').Options} */ (_options); @@ -60,8 +58,7 @@ options._render = vnode => { if (hookItem._nextValue) { hookItem._value = hookItem._nextValue; } - hookItem._pendingValue = EMPTY; - hookItem._nextValue = hookItem._pendingArgs = undefined; + hookItem._pendingArgs = hookItem._nextValue = undefined; }); } else { hooks._pendingEffects.forEach(invokeCleanup); @@ -84,11 +81,7 @@ options.diffed = vnode => { if (hookItem._pendingArgs) { hookItem._args = hookItem._pendingArgs; } - if (hookItem._pendingValue !== EMPTY) { - hookItem._value = hookItem._pendingValue; - } hookItem._pendingArgs = undefined; - hookItem._pendingValue = EMPTY; }); } previousComponent = currentComponent = null; @@ -159,7 +152,7 @@ function getHookState(index, type) { }); if (index >= hooks._list.length) { - hooks._list.push({ _pendingValue: EMPTY }); + hooks._list.push({}); } return hooks._list[index]; @@ -350,10 +343,9 @@ export function useMemo(factory, args) { /** @type {import('./internal').MemoHookState} */ const state = getHookState(currentIndex++, 7); if (argsChanged(state._args, args)) { - state._pendingValue = factory(); - state._pendingArgs = args; + state._value = factory(); + state._args = args; state._factory = factory; - return state._pendingValue; } return state._value; diff --git a/hooks/test/browser/combinations.test.js b/hooks/test/browser/combinations.test.js index 3b7cc51ab1..fff51cd80a 100644 --- a/hooks/test/browser/combinations.test.js +++ b/hooks/test/browser/combinations.test.js @@ -477,4 +477,44 @@ describe('combinations', () => { 'anchor effect' ]); }); + + it('should not loop infinitely', () => { + const actions = []; + let toggle; + function App() { + const [value, setValue] = useState(false); + + const data = useMemo(() => { + actions.push('memo'); + return {}; + }, [value]); + + const [prevData, setPreviousData] = useState(data); + if (prevData !== data) { + setPreviousData(data); + } + + actions.push('render'); + toggle = () => setValue(!value); + return
Value: {JSON.stringify(value)}
; + } + + act(() => { + render(, scratch); + }); + expect(actions).to.deep.equal(['memo', 'render']); + expect(scratch.innerHTML).to.deep.equal('
Value: false
'); + + act(() => { + toggle(); + }); + expect(actions).to.deep.equal([ + 'memo', + 'render', + 'memo', + 'render', + 'render' + ]); + expect(scratch.innerHTML).to.deep.equal('
Value: true
'); + }); }); diff --git a/hooks/test/browser/useMemo.test.js b/hooks/test/browser/useMemo.test.js index cabec387bb..20b55cf5af 100644 --- a/hooks/test/browser/useMemo.test.js +++ b/hooks/test/browser/useMemo.test.js @@ -151,12 +151,13 @@ describe('useMemo', () => { act(() => { set('bye'); }); - expect(calls.length).to.equal(5); + expect(calls.length).to.equal(6); expect(calls).to.deep.equal([ 'doing memo', 'render hi', 'doing memo', - 'render bye', // We expect a missing "doing memo" here because we return to the previous args value + 'render bye', + 'doing memo', 'render hi' ]); }); diff --git a/mangle.json b/mangle.json index 7744675ce2..fcd2dce74f 100644 --- a/mangle.json +++ b/mangle.json @@ -31,7 +31,6 @@ "$_list": "__", "$_pendingEffects": "__h", "$_value": "__", - "$_pendingValue": "__V", "$_nextValue": "__N", "$_original": "__v", "$_args": "__H",