diff --git a/src/useObserver.ts b/src/useObserver.ts index cfeaa648..cc53d45f 100644 --- a/src/useObserver.ts +++ b/src/useObserver.ts @@ -1,8 +1,8 @@ import { Reaction } from "mobx" -import { useDebugValue, useEffect, useRef } from "react" +import { useDebugValue, useRef } from "react" import { printDebugValue } from "./printDebugValue" import { isUsingStaticRendering } from "./staticRendering" -import { useForceUpdate } from "./utils" +import { useForceUpdate, useUnmount } from "./utils" export type ForceUpdateHook = () => () => void @@ -25,25 +25,17 @@ export function useObserver( const forceUpdate = wantedForceUpdateHook() const reaction = useRef(null) - const committed = useRef(false) - if (!reaction.current) { - // First render for this component. Not yet committed. reaction.current = new Reaction(`observer(${baseComponentName})`, () => { - // Observable has changed. Only force an update if we've definitely - // been committed. - if (committed.current) { - forceUpdate() - } + forceUpdate() }) } useDebugValue(reaction, printDebugValue) - useEffect(() => { - committed.current = true - return () => reaction.current!.dispose() - }, []) + useUnmount(() => { + reaction.current!.dispose() + }) // render the original component, but have the // reaction track the observables, so that rendering diff --git a/src/utils.ts b/src/utils.ts index a09164ec..d2dc0816 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,4 +1,10 @@ -import { useCallback, useState } from "react" +import { useCallback, useEffect, useState } from "react" + +const EMPTY_ARRAY: any[] = [] + +export function useUnmount(fn: () => void) { + useEffect(() => fn, EMPTY_ARRAY) +} export function useForceUpdate() { const [, setTick] = useState(0) diff --git a/test/useObserver.test.tsx b/test/useObserver.test.tsx deleted file mode 100644 index 2e8be20c..00000000 --- a/test/useObserver.test.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import mockConsole from "jest-mock-console" -import * as mobx from "mobx" -import * as React from "react" -import { act, cleanup, render } from "react-testing-library" - -import { useObserver } from "../src" - -afterEach(cleanup) - -test("uncommitted observing components should not attempt state changes", () => { - const store = mobx.observable({ count: 0 }) - - const TestComponent = () => useObserver(() =>
{store.count}
) - - // Render our observing component wrapped in StrictMode - const rendering = render( - - - - ) - - // That will have caused our component to have been rendered - // more than once, but when we unmount it'll only unmount once. - rendering.unmount() - - // Trigger a change to the observable. If the reactions were - // not disposed correctly, we'll see some console errors from - // React StrictMode because we're calling state mutators to - // trigger an update. - const restoreConsole = mockConsole() - try { - act(() => { - store.count++ - }) - - // Check to see if any console errors were reported. - // tslint:disable-next-line: no-console - expect(console.error).not.toHaveBeenCalled() - } finally { - restoreConsole() - } -})