Introduce ComponentState (#4386)
* Proof of concept ComponentState * Migrate to createState and createFamilyState * Refactor * Fix * Fix tests * Fix lint * Fix tests * Re-enable coverage
This commit is contained in:
@ -1,64 +0,0 @@
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import { RecoilRoot, RecoilState } from 'recoil';
|
||||
import { undefined } from 'zod';
|
||||
|
||||
import { useRecoilScopedFamilyState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedFamilyState';
|
||||
import { FamilyStateScopeMapKey } from '@/ui/utilities/recoil-scope/scopes-internal/types/FamilyStateScopeMapKey';
|
||||
import { createFamilyStateScopeMap } from '@/ui/utilities/recoil-scope/utils/createFamilyStateScopeMap';
|
||||
|
||||
const testState = createFamilyStateScopeMap({
|
||||
key: 'sampleKey',
|
||||
defaultValue: 'defaultValue',
|
||||
});
|
||||
|
||||
describe('useRecoilScopedFamilyState', () => {
|
||||
it('Should work as expected', async () => {
|
||||
const { result, rerender } = renderHook(
|
||||
({
|
||||
recoilState,
|
||||
scopeId,
|
||||
familyKey,
|
||||
}: {
|
||||
recoilState: (
|
||||
scopedFamilyKey: FamilyStateScopeMapKey<string>,
|
||||
) => RecoilState<string>;
|
||||
scopeId: string;
|
||||
familyKey?: string;
|
||||
}) => useRecoilScopedFamilyState(recoilState, scopeId, familyKey),
|
||||
{
|
||||
wrapper: RecoilRoot,
|
||||
initialProps: {
|
||||
recoilState: testState,
|
||||
scopeId: 'scopeId',
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
expect(result.current).toEqual([undefined, undefined]);
|
||||
|
||||
rerender({
|
||||
recoilState: testState,
|
||||
scopeId: 'scopeId',
|
||||
familyKey: 'familyKey',
|
||||
});
|
||||
|
||||
const [value, setValue] = result.current;
|
||||
|
||||
expect(value).toBe('defaultValue');
|
||||
expect(setValue).toBeInstanceOf(Function);
|
||||
|
||||
act(() => {
|
||||
setValue?.('newValue');
|
||||
});
|
||||
|
||||
expect(result.current[0]).toBe('newValue');
|
||||
|
||||
rerender({
|
||||
recoilState: testState,
|
||||
scopeId: 'scopeId1',
|
||||
familyKey: 'familyKey',
|
||||
});
|
||||
|
||||
expect(result.current[0]).toBe('defaultValue');
|
||||
});
|
||||
});
|
||||
@ -1,50 +0,0 @@
|
||||
import { createContext } from 'react';
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import { atomFamily, RecoilRoot } from 'recoil';
|
||||
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
|
||||
const testScopedState = atomFamily<string | null, string>({
|
||||
key: 'testKey',
|
||||
default: null,
|
||||
});
|
||||
|
||||
const mockedContextValue = 'mocked-scope-id';
|
||||
const MockedContext = createContext<string | null>(mockedContextValue);
|
||||
const nullContext = createContext<string | null>(null);
|
||||
|
||||
const ERROR_MESSAGE =
|
||||
'Using a scoped atom without a RecoilScope : testKey__"", verify that you are using a RecoilScope with a specific context if you intended to do so.';
|
||||
|
||||
describe('useRecoilScopedState', () => {
|
||||
it('Should return the getter and setter for the state and context passed and work properly', async () => {
|
||||
const { result } = renderHook(
|
||||
() => useRecoilScopedState(testScopedState, MockedContext),
|
||||
{
|
||||
wrapper: ({ children }) => (
|
||||
<MockedContext.Provider value={mockedContextValue}>
|
||||
<RecoilRoot>{children}</RecoilRoot>
|
||||
</MockedContext.Provider>
|
||||
),
|
||||
},
|
||||
);
|
||||
|
||||
const [scopedState, setScopedState] = result.current;
|
||||
|
||||
expect(scopedState).toBeNull();
|
||||
|
||||
await act(async () => {
|
||||
setScopedState('testValue');
|
||||
});
|
||||
|
||||
const [scopedStateAfterSetter] = result.current;
|
||||
|
||||
expect(scopedStateAfterSetter).toEqual('testValue');
|
||||
});
|
||||
|
||||
it('Should throw an error when the recoilScopeId is not found by the context', () => {
|
||||
expect(() => {
|
||||
renderHook(() => useRecoilScopedState(testScopedState, nullContext));
|
||||
}).toThrow(ERROR_MESSAGE);
|
||||
});
|
||||
});
|
||||
@ -1,42 +0,0 @@
|
||||
import { createContext } from 'react';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { atomFamily, RecoilRoot } from 'recoil';
|
||||
|
||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||
|
||||
const testScopedState = atomFamily<string | null, string>({
|
||||
key: 'testKey',
|
||||
default: null,
|
||||
});
|
||||
|
||||
const mockedContextValue = 'mocked-scope-id';
|
||||
const MockedContext = createContext<string | null>(mockedContextValue);
|
||||
const nullContext = createContext<string | null>(null);
|
||||
|
||||
const ERROR_MESSAGE =
|
||||
'Using a scoped atom without a RecoilScope : testKey__"", verify that you are using a RecoilScope with a specific context if you intended to do so.';
|
||||
|
||||
describe('useRecoilScopedValue', () => {
|
||||
it('Should return the getter and setter for the state and context passed and work properly', async () => {
|
||||
const { result } = renderHook(
|
||||
() => useRecoilScopedValue(testScopedState, MockedContext),
|
||||
{
|
||||
wrapper: ({ children }) => (
|
||||
<MockedContext.Provider value={mockedContextValue}>
|
||||
<RecoilRoot>{children}</RecoilRoot>
|
||||
</MockedContext.Provider>
|
||||
),
|
||||
},
|
||||
);
|
||||
|
||||
const scopedState = result.current;
|
||||
|
||||
expect(scopedState).toBeNull();
|
||||
});
|
||||
|
||||
it('Should throw an error when the recoilScopeId is not found by the context', () => {
|
||||
expect(() => {
|
||||
renderHook(() => useRecoilScopedValue(testScopedState, nullContext));
|
||||
}).toThrow(ERROR_MESSAGE);
|
||||
});
|
||||
});
|
||||
@ -1,27 +0,0 @@
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { atomFamily, RecoilRoot } from 'recoil';
|
||||
|
||||
import { useRecoilScopedValueV2 } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValueV2';
|
||||
import { StateScopeMapKey } from '@/ui/utilities/recoil-scope/scopes-internal/types/StateScopeMapKey';
|
||||
|
||||
const scopedAtom = atomFamily<string, StateScopeMapKey>({
|
||||
key: 'scopedAtomKey',
|
||||
default: 'initialValue',
|
||||
});
|
||||
|
||||
describe('useRecoilScopedValueV2', () => {
|
||||
const mockedScopeId = 'mocked-scope-id';
|
||||
|
||||
it('Should return the scoped value using useRecoilScopedValueV2', () => {
|
||||
const { result } = renderHook(
|
||||
() => useRecoilScopedValueV2(scopedAtom, mockedScopeId),
|
||||
{
|
||||
wrapper: RecoilRoot,
|
||||
},
|
||||
);
|
||||
|
||||
const scopedValue = result.current;
|
||||
|
||||
expect(scopedValue).toBe('initialValue');
|
||||
});
|
||||
});
|
||||
@ -1,79 +0,0 @@
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import { atomFamily, RecoilRoot } from 'recoil';
|
||||
|
||||
import { useRecoilScopedFamilyState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedFamilyState';
|
||||
import { useSetRecoilScopedFamilyState } from '@/ui/utilities/recoil-scope/hooks/useSetRecoilScopedFamilyState';
|
||||
import { FamilyStateScopeMapKey } from '@/ui/utilities/recoil-scope/scopes-internal/types/FamilyStateScopeMapKey';
|
||||
|
||||
const mockedScopedFamilyState = atomFamily<
|
||||
string,
|
||||
FamilyStateScopeMapKey<string>
|
||||
>({
|
||||
key: 'scopedAtomKey',
|
||||
default: 'initialValue',
|
||||
});
|
||||
|
||||
describe('useSetRecoilScopedFamilyState', () => {
|
||||
const mockedScopeId = 'mocked-scope-id';
|
||||
const mockedFamilyKey = 'test-key-value';
|
||||
|
||||
it('Should return a setter that updates the state value and work properly', async () => {
|
||||
const useCombinedHooks = () => {
|
||||
const setRecoilScopedFamilyState = useSetRecoilScopedFamilyState(
|
||||
mockedScopedFamilyState,
|
||||
mockedScopeId,
|
||||
mockedFamilyKey,
|
||||
);
|
||||
|
||||
const [mocked] = useRecoilScopedFamilyState(
|
||||
mockedScopedFamilyState,
|
||||
mockedScopeId,
|
||||
mockedFamilyKey,
|
||||
);
|
||||
|
||||
return {
|
||||
setRecoilScopedFamilyState,
|
||||
scopedFamilyState: mocked,
|
||||
};
|
||||
};
|
||||
|
||||
const { result } = renderHook(() => useCombinedHooks(), {
|
||||
wrapper: RecoilRoot,
|
||||
});
|
||||
|
||||
expect(result.current.scopedFamilyState).toBe('initialValue');
|
||||
expect(result.current.setRecoilScopedFamilyState).toBeInstanceOf(Function);
|
||||
|
||||
await act(async () => {
|
||||
result.current.setRecoilScopedFamilyState?.('testValue');
|
||||
});
|
||||
|
||||
expect(result.current.scopedFamilyState).toBe('testValue');
|
||||
});
|
||||
|
||||
it('Should return undefined when familyKey is missing', async () => {
|
||||
const useCombinedHooks = () => {
|
||||
const setRecoilScopedFamilyState = useSetRecoilScopedFamilyState(
|
||||
mockedScopedFamilyState,
|
||||
mockedScopeId,
|
||||
);
|
||||
|
||||
const [mocked] = useRecoilScopedFamilyState(
|
||||
mockedScopedFamilyState,
|
||||
mockedScopeId,
|
||||
);
|
||||
|
||||
return {
|
||||
setRecoilScopedFamilyState,
|
||||
scopedFamilyState: mocked,
|
||||
};
|
||||
};
|
||||
|
||||
const { result } = renderHook(() => useCombinedHooks(), {
|
||||
wrapper: RecoilRoot,
|
||||
});
|
||||
|
||||
expect(result.current.scopedFamilyState).toBeUndefined();
|
||||
expect(result.current.setRecoilScopedFamilyState).toBeUndefined();
|
||||
});
|
||||
});
|
||||
@ -1,47 +0,0 @@
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import { atomFamily, RecoilRoot } from 'recoil';
|
||||
|
||||
import { useRecoilScopedValueV2 } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValueV2';
|
||||
import { useSetRecoilScopedStateV2 } from '@/ui/utilities/recoil-scope/hooks/useSetRecoilScopedStateV2';
|
||||
import { StateScopeMapKey } from '@/ui/utilities/recoil-scope/scopes-internal/types/StateScopeMapKey';
|
||||
|
||||
const scopedAtom = atomFamily<string, StateScopeMapKey>({
|
||||
key: 'scopedAtomKey',
|
||||
default: 'initialValue',
|
||||
});
|
||||
|
||||
describe('useSetRecoilScopedStateV2', () => {
|
||||
const mockedScopeId = 'mocked-scope-id';
|
||||
|
||||
it('Should return a setter that updates the state value', async () => {
|
||||
const useCombinedHooks = () => {
|
||||
const setRecoilScopedStateV2 = useSetRecoilScopedStateV2(
|
||||
scopedAtom,
|
||||
mockedScopeId,
|
||||
);
|
||||
|
||||
const recoilScopedStateValue = useRecoilScopedValueV2(
|
||||
scopedAtom,
|
||||
mockedScopeId,
|
||||
);
|
||||
|
||||
return {
|
||||
setRecoilScopedStateV2,
|
||||
recoilScopedStateValue,
|
||||
};
|
||||
};
|
||||
|
||||
const { result } = renderHook(() => useCombinedHooks(), {
|
||||
wrapper: RecoilRoot,
|
||||
});
|
||||
|
||||
expect(result.current.recoilScopedStateValue).toBe('initialValue');
|
||||
expect(result.current.setRecoilScopedStateV2).toBeInstanceOf(Function);
|
||||
|
||||
await act(async () => {
|
||||
result.current.setRecoilScopedStateV2('testValue');
|
||||
});
|
||||
|
||||
expect(result.current.recoilScopedStateValue).toBe('testValue');
|
||||
});
|
||||
});
|
||||
@ -1,27 +0,0 @@
|
||||
import { RecoilState, SerializableParam, useRecoilState } from 'recoil';
|
||||
|
||||
import { FamilyStateScopeMapKey } from '../scopes-internal/types/FamilyStateScopeMapKey';
|
||||
|
||||
export const useRecoilScopedFamilyState = <
|
||||
StateType,
|
||||
FamilyKey extends SerializableParam,
|
||||
>(
|
||||
recoilState: (
|
||||
scopedFamilyKey: FamilyStateScopeMapKey<FamilyKey>,
|
||||
) => RecoilState<StateType>,
|
||||
scopeId: string,
|
||||
familyKey?: FamilyKey,
|
||||
) => {
|
||||
const familyState = useRecoilState<StateType>(
|
||||
recoilState({
|
||||
scopeId,
|
||||
familyKey: familyKey || ('' as FamilyKey),
|
||||
}),
|
||||
);
|
||||
|
||||
if (!familyKey) {
|
||||
return [undefined, undefined];
|
||||
}
|
||||
|
||||
return familyState;
|
||||
};
|
||||
@ -1,22 +0,0 @@
|
||||
import { Context, useContext } from 'react';
|
||||
import { RecoilState, useRecoilState } from 'recoil';
|
||||
|
||||
import { RecoilScopeContext } from '../states/RecoilScopeContext';
|
||||
|
||||
export const useRecoilScopedState = <StateType>(
|
||||
recoilState: (param: string) => RecoilState<StateType>,
|
||||
CustomRecoilScopeContext?: Context<string | null>,
|
||||
) => {
|
||||
const recoilScopeId = useContext(
|
||||
CustomRecoilScopeContext ?? RecoilScopeContext,
|
||||
);
|
||||
|
||||
if (!recoilScopeId)
|
||||
throw new Error(
|
||||
`Using a scoped atom without a RecoilScope : ${
|
||||
recoilState('').key
|
||||
}, verify that you are using a RecoilScope with a specific context if you intended to do so.`,
|
||||
);
|
||||
|
||||
return useRecoilState<StateType>(recoilState(recoilScopeId));
|
||||
};
|
||||
@ -1,9 +1,9 @@
|
||||
import { RecoilState, useRecoilState } from 'recoil';
|
||||
|
||||
import { StateScopeMapKey } from '../scopes-internal/types/StateScopeMapKey';
|
||||
import { ComponentStateKey } from '@/ui/utilities/state/component-state/types/ComponentStateKey';
|
||||
|
||||
export const useRecoilScopedStateV2 = <StateType>(
|
||||
recoilState: (scopedKey: StateScopeMapKey) => RecoilState<StateType>,
|
||||
recoilState: (scopedKey: ComponentStateKey) => RecoilState<StateType>,
|
||||
scopeId: string,
|
||||
) => {
|
||||
return useRecoilState<StateType>(
|
||||
|
||||
@ -1,25 +0,0 @@
|
||||
import { Context, useContext } from 'react';
|
||||
import { RecoilState, RecoilValueReadOnly, useRecoilValue } from 'recoil';
|
||||
|
||||
import { RecoilScopeContext } from '../states/RecoilScopeContext';
|
||||
|
||||
/**
|
||||
* @deprecated use useRecoilScopedStateV2 instead
|
||||
*/
|
||||
export const useRecoilScopedValue = <T>(
|
||||
recoilState: (param: string) => RecoilState<T> | RecoilValueReadOnly<T>,
|
||||
CustomRecoilScopeContext?: Context<string | null>,
|
||||
) => {
|
||||
const recoilScopeId = useContext(
|
||||
CustomRecoilScopeContext ?? RecoilScopeContext,
|
||||
);
|
||||
|
||||
if (!recoilScopeId)
|
||||
throw new Error(
|
||||
`Using a scoped atom without a RecoilScope : ${
|
||||
recoilState('').key
|
||||
}, verify that you are using a RecoilScope with a specific context if you intended to do so.`,
|
||||
);
|
||||
|
||||
return useRecoilValue<T>(recoilState(recoilScopeId));
|
||||
};
|
||||
@ -1,14 +0,0 @@
|
||||
import { RecoilState, useRecoilValue } from 'recoil';
|
||||
|
||||
import { StateScopeMapKey } from '../scopes-internal/types/StateScopeMapKey';
|
||||
|
||||
export const useRecoilScopedValueV2 = <StateType>(
|
||||
recoilState: (scopedKey: StateScopeMapKey) => RecoilState<StateType>,
|
||||
scopeId: string,
|
||||
) => {
|
||||
return useRecoilValue<StateType>(
|
||||
recoilState({
|
||||
scopeId,
|
||||
}),
|
||||
);
|
||||
};
|
||||
@ -1,27 +0,0 @@
|
||||
import { RecoilState, SerializableParam, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { FamilyStateScopeMapKey } from '../scopes-internal/types/FamilyStateScopeMapKey';
|
||||
|
||||
export const useSetRecoilScopedFamilyState = <
|
||||
StateType,
|
||||
FamilyKey extends SerializableParam,
|
||||
>(
|
||||
recoilState: (
|
||||
scopedFamilyKey: FamilyStateScopeMapKey<FamilyKey>,
|
||||
) => RecoilState<StateType>,
|
||||
scopeId: string,
|
||||
familyKey?: FamilyKey,
|
||||
) => {
|
||||
const familyState = useSetRecoilState<StateType>(
|
||||
recoilState({
|
||||
scopeId,
|
||||
familyKey: familyKey || ('' as FamilyKey),
|
||||
}),
|
||||
);
|
||||
|
||||
if (!familyKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
return familyState;
|
||||
};
|
||||
@ -1,14 +0,0 @@
|
||||
import { RecoilState, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { StateScopeMapKey } from '../scopes-internal/types/StateScopeMapKey';
|
||||
|
||||
export const useSetRecoilScopedStateV2 = <StateType>(
|
||||
recoilState: (scopedKey: StateScopeMapKey) => RecoilState<StateType>,
|
||||
scopeId: string,
|
||||
) => {
|
||||
return useSetRecoilState<StateType>(
|
||||
recoilState({
|
||||
scopeId,
|
||||
}),
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user