From 382efc6f8453fc364730141563738eaaf31880e8 Mon Sep 17 00:00:00 2001 From: Seyyed Morteza Moosavi Date: Fri, 11 Dec 2020 12:45:31 +0330 Subject: [PATCH] feat(state-wire): support rewiring (#28) * feat(state-wire): support rewiring * feat(use-wire-value): support rewiring --- src/state-wire/use-state-wire.ts | 33 +++++++---- src/state-wire/use-wire-state.test.ts | 85 +++++++++++++++++++++++++++ src/state-wire/use-wire-state.ts | 2 - src/state-wire/use-wire-value.test.ts | 55 +++++++++++++++++ src/state-wire/use-wire-value.ts | 2 - 5 files changed, 162 insertions(+), 15 deletions(-) diff --git a/src/state-wire/use-state-wire.ts b/src/state-wire/use-state-wire.ts index acb923b..7e99ec2 100644 --- a/src/state-wire/use-state-wire.ts +++ b/src/state-wire/use-state-wire.ts @@ -1,9 +1,23 @@ -import { useEffect, useState } from 'react'; +import { useEffect, useRef, useState } from 'react'; import { InitializerOrValue, isInitializer } from '../utils/is-initializer'; import { isDefined } from '../utils/type-utils'; import { createStateWire } from './create-state-wire'; import { StateWire } from './state-wire'; +const create = ( + upLink: StateWire | null | undefined, + initialValue: InitializerOrValue, +) => { + return createStateWire( + upLink || {}, + isDefined(initialValue) + ? isInitializer(initialValue) + ? initialValue() + : initialValue + : undefined, + ); +}; + export function useStateWire(upLink: StateWire): StateWire; export function useStateWire( upLink: StateWire | null | undefined, @@ -17,16 +31,13 @@ export function useStateWire( upLink: StateWire | null | undefined, initialValue?: InitializerOrValue, ): StateWire | StateWire { - const [[wire, connect]] = useState(() => { - return createStateWire( - upLink || {}, - isDefined(initialValue) - ? isInitializer(initialValue) - ? initialValue() - : initialValue - : undefined, - ); - }); + const [[wire, connect], set] = useState(() => create(upLink, initialValue)); + const lastUpLinkRef = useRef(upLink); + + if (lastUpLinkRef.current !== upLink) { + lastUpLinkRef.current = upLink; + set(create(upLink, wire.getValue())); + } useEffect(() => { return connect(); }, [connect]); diff --git a/src/state-wire/use-wire-state.test.ts b/src/state-wire/use-wire-state.test.ts index faf18d9..ba5e65a 100644 --- a/src/state-wire/use-wire-state.test.ts +++ b/src/state-wire/use-wire-state.test.ts @@ -287,4 +287,89 @@ describe('useWireState', () => { expect(result.current.value2).toBe(wireValue); }); }); + + describe('when up-link wire changed', () => { + it('should return the new wire value', () => { + const { result, rerender } = renderHook( + ({ firstWire }: { firstWire: boolean }) => { + const wire1 = useStateWire(null, 5); + const wire2 = useStateWire(null, 6); + const [value] = useWireState(firstWire ? wire1 : wire2); + return { value }; + }, + { initialProps: { firstWire: true } }, + ); + rerender({ firstWire: false }); + expect(result.current.value).toBe(6); + }); + it('should return update the new wire value if it is undefined', () => { + const { result, rerender } = renderHook( + ({ firstWire }: { firstWire: boolean }) => { + const wire1 = useStateWire(null, 5); + const wire2 = useStateWire(null, undefined); + useWireState(firstWire ? wire1 : wire2); + return { wire2 }; + }, + { initialProps: { firstWire: true } }, + ); + rerender({ firstWire: false }); + + expect(result.current.wire2.getValue()).toBe(5); + }); + + it('should be updated with the new wire', () => { + const { result, rerender } = renderHook( + ({ firstWire }: { firstWire: boolean }) => { + const wire1 = useStateWire(null, 5); + const wire2 = useStateWire(null, 6); + const [value] = useWireState(firstWire ? wire1 : wire2); + return { value, wire1, wire2 }; + }, + { initialProps: { firstWire: true } }, + ); + rerender({ firstWire: false }); + act(() => { + result.current.wire2.setValue(7); + }); + expect(result.current.value).toBe(7); + expect(result.current.wire1.getValue()).toBe(5); + }); + + it('should not be updated with the old wire', () => { + const { result, rerender } = renderHook( + ({ firstWire }: { firstWire: boolean }) => { + const wire1 = useStateWire(null, 5); + const wire2 = useStateWire(null, 6); + const [value] = useWireState(firstWire ? wire1 : wire2); + return { value, wire1, wire2 }; + }, + { initialProps: { firstWire: true } }, + ); + rerender({ firstWire: false }); + act(() => { + result.current.wire1.setValue(7); + }); + expect(result.current.value).toBe(6); + expect(result.current.wire2.getValue()).toBe(6); + }); + + it('should only updated the new wire', () => { + const { result, rerender } = renderHook( + ({ firstWire }: { firstWire: boolean }) => { + const wire1 = useStateWire(null, 5); + const wire2 = useStateWire(null, 6); + const [value, setValue] = useWireState(firstWire ? wire1 : wire2); + return { value, setValue, wire1, wire2 }; + }, + { initialProps: { firstWire: true } }, + ); + rerender({ firstWire: false }); + act(() => { + result.current.setValue(7); + }); + expect(result.current.value).toBe(7); + expect(result.current.wire1.getValue()).toBe(5); + expect(result.current.wire2.getValue()).toBe(7); + }); + }); }); diff --git a/src/state-wire/use-wire-state.ts b/src/state-wire/use-wire-state.ts index a0768f2..214d1ff 100644 --- a/src/state-wire/use-wire-state.ts +++ b/src/state-wire/use-wire-state.ts @@ -9,7 +9,6 @@ import { import { InitializerOrValue } from '../utils/is-initializer'; import { isSetStateAction } from '../utils/is-set-state-action'; import { Defined, isDefined } from '../utils/type-utils'; -import { useStabilityGuard } from '../utils/use-stability-guard'; import { StateWire } from './state-wire'; import { useStateWire } from './use-state-wire'; @@ -32,7 +31,6 @@ export function useWireState( wire: StateWire | null | undefined, initialValue?: InitializerOrValue, ): valueAndAction | valueAndAction { - useStabilityGuard(wire); const innerWire = useStateWire(wire, initialValue); const valueToReturn = innerWire.getValue(); const [stateValue, setStateValue] = useState(() => innerWire.getValue()); diff --git a/src/state-wire/use-wire-value.test.ts b/src/state-wire/use-wire-value.test.ts index 9d6a9ba..ba6d91b 100644 --- a/src/state-wire/use-wire-value.test.ts +++ b/src/state-wire/use-wire-value.test.ts @@ -82,4 +82,59 @@ describe('useWireValue', () => { expect(result.current.value).toBe(6); }); }); + describe('when wire changed', () => { + it('should use the new wire value', () => { + const { result, rerender } = renderHook( + ({ firstWire }: { firstWire: boolean }) => { + const wire1 = useStateWire(null, 5); + const wire2 = useStateWire(null, 6); + const value = useWireValue(firstWire ? wire1 : wire2); + return { value, wire1, wire2 }; + }, + { initialProps: { firstWire: true } }, + ); + rerender({ firstWire: false }); + expect(result.current.value).toBe(6); + expect(result.current.wire1.getValue()).toBe(5); + expect(result.current.wire2.getValue()).toBe(6); + }); + it('should updated when new wire changed', () => { + const { result, rerender } = renderHook( + ({ firstWire }: { firstWire: boolean }) => { + const wire1 = useStateWire(null, 5); + const wire2 = useStateWire(null, 6); + const value = useWireValue(firstWire ? wire1 : wire2); + return { value, wire1, wire2 }; + }, + { initialProps: { firstWire: true } }, + ); + rerender({ firstWire: false }); + + act(() => { + result.current.wire2.setValue(7); + }); + expect(result.current.value).toBe(7); + expect(result.current.wire1.getValue()).toBe(5); + expect(result.current.wire2.getValue()).toBe(7); + }); + it('should not updated when old wire changed', () => { + const { result, rerender } = renderHook( + ({ firstWire }: { firstWire: boolean }) => { + const wire1 = useStateWire(null, 5); + const wire2 = useStateWire(null, 6); + const value = useWireValue(firstWire ? wire1 : wire2); + return { value, wire1, wire2 }; + }, + { initialProps: { firstWire: true } }, + ); + rerender({ firstWire: false }); + + act(() => { + result.current.wire1.setValue(7); + }); + expect(result.current.value).toBe(6); + expect(result.current.wire1.getValue()).toBe(7); + expect(result.current.wire2.getValue()).toBe(6); + }); + }); }); diff --git a/src/state-wire/use-wire-value.ts b/src/state-wire/use-wire-value.ts index fed606a..bab7cc0 100644 --- a/src/state-wire/use-wire-value.ts +++ b/src/state-wire/use-wire-value.ts @@ -1,6 +1,5 @@ import { useDebugValue, useEffect, useState } from 'react'; import { Defined } from '../utils/type-utils'; -import { useStabilityGuard } from '../utils/use-stability-guard'; import { ReadonlyStateWire, WireState } from './readonly-state-wire'; export function useWireValue( @@ -27,7 +26,6 @@ export function useWireValue>( defaultValue?: WireState, ): WireState | undefined { type Value = WireState; - useStabilityGuard(wire); const wireValue = wire?.getValue(); const valueToReturn = wireValue === undefined ? defaultValue : wireValue; useDebugValue(valueToReturn);