Skip to content

Commit

Permalink
feat(state-wire): support rewiring (#28)
Browse files Browse the repository at this point in the history
* feat(state-wire): support rewiring

* feat(use-wire-value): support rewiring
  • Loading branch information
smmoosavi authored Dec 11, 2020
1 parent 0ca7e49 commit 382efc6
Show file tree
Hide file tree
Showing 5 changed files with 162 additions and 15 deletions.
33 changes: 22 additions & 11 deletions src/state-wire/use-state-wire.ts
Original file line number Diff line number Diff line change
@@ -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 = <V>(
upLink: StateWire<V | undefined> | null | undefined,
initialValue: InitializerOrValue<V | undefined>,
) => {
return createStateWire<V>(
upLink || {},
isDefined(initialValue)
? isInitializer<V | undefined>(initialValue)
? initialValue()
: initialValue
: undefined,
);
};

export function useStateWire<V>(upLink: StateWire<V>): StateWire<V>;
export function useStateWire<V>(
upLink: StateWire<V | undefined> | null | undefined,
Expand All @@ -17,16 +31,13 @@ export function useStateWire<V>(
upLink: StateWire<V | undefined> | null | undefined,
initialValue?: InitializerOrValue<V | undefined>,
): StateWire<V | undefined> | StateWire<V> {
const [[wire, connect]] = useState(() => {
return createStateWire<V>(
upLink || {},
isDefined(initialValue)
? isInitializer<V | undefined>(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]);
Expand Down
85 changes: 85 additions & 0 deletions src/state-wire/use-wire-state.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<number | undefined>(null, 5);
const wire2 = useStateWire<number | undefined>(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);
});
});
});
2 changes: 0 additions & 2 deletions src/state-wire/use-wire-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -32,7 +31,6 @@ export function useWireState<V>(
wire: StateWire<V | undefined> | null | undefined,
initialValue?: InitializerOrValue<V | undefined>,
): valueAndAction<V | undefined> | valueAndAction<V> {
useStabilityGuard(wire);
const innerWire = useStateWire(wire, initialValue);
const valueToReturn = innerWire.getValue();
const [stateValue, setStateValue] = useState(() => innerWire.getValue());
Expand Down
55 changes: 55 additions & 0 deletions src/state-wire/use-wire-value.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
});
});
2 changes: 0 additions & 2 deletions src/state-wire/use-wire-value.ts
Original file line number Diff line number Diff line change
@@ -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(
Expand All @@ -27,7 +26,6 @@ export function useWireValue<W extends ReadonlyStateWire<any>>(
defaultValue?: WireState<W>,
): WireState<W> | undefined {
type Value = WireState<W>;
useStabilityGuard(wire);
const wireValue = wire?.getValue();
const valueToReturn = wireValue === undefined ? defaultValue : wireValue;
useDebugValue(valueToReturn);
Expand Down

0 comments on commit 382efc6

Please sign in to comment.