UNPKG

recoil

Version:

Recoil - A state management library for React

362 lines (355 loc) 9.1 kB
/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @emails oncall+recoil * @flow strict-local * @format */ 'use strict'; import type { RecoilValue } from '../../core/Recoil_RecoilValue'; const { getRecoilTestFn } = require('../../__test_utils__/Recoil_TestingUtils'); let atom, DefaultValue, selectorFamily, getRecoilValueAsLoadable, setRecoilValue, store, myAtom; const testRecoil = getRecoilTestFn(() => { const { makeStore } = require('../../__test_utils__/Recoil_TestingUtils'); atom = require('../Recoil_atom'); ({ DefaultValue } = require('../../core/Recoil_Node')); selectorFamily = require('../Recoil_selectorFamily'); ({ getRecoilValueAsLoadable, setRecoilValue } = require('../../core/Recoil_RecoilValueInterface')); store = makeStore(); myAtom = atom({ key: 'atom', default: 0 }); }); declare function getValue<T>(recoilValue: RecoilValue<T>): T; declare function set(recoilValue: any, value: any): any; testRecoil('selectorFamily - number parameter', () => { const mySelector = selectorFamily({ key: 'selectorFamily/number', get: multiplier => ({ get }) => get(myAtom) * multiplier }); set(myAtom, 1); expect(getValue(mySelector(10))).toBe(10); expect(getValue(mySelector(100))).toBe(100); set(myAtom, 2); expect(getValue(mySelector(10))).toBe(20); expect(getValue(mySelector(100))).toBe(200); }); testRecoil('selectorFamily - array parameter', () => { const mySelector = selectorFamily({ key: 'selectorFamily/array', get: numbers => () => numbers.reduce((x, y) => x + y, 0) }); expect(getValue(mySelector([]))).toBe(0); expect(getValue(mySelector([1, 2, 3]))).toBe(6); expect(getValue(mySelector([0, 1, 1, 2, 3, 5]))).toBe(12); }); testRecoil('selectorFamily - object parameter', () => { const mySelector = selectorFamily({ key: 'selectorFamily/object', get: ({ multiplier }) => ({ get }) => get(myAtom) * multiplier }); set(myAtom, 1); expect(getValue(mySelector({ multiplier: 10 }))).toBe(10); expect(getValue(mySelector({ multiplier: 100 }))).toBe(100); set(myAtom, 2); expect(getValue(mySelector({ multiplier: 10 }))).toBe(20); expect(getValue(mySelector({ multiplier: 100 }))).toBe(200); }); testRecoil('selectorFamily - date parameter', () => { const mySelector = selectorFamily({ key: 'selectorFamily/date', get: date => ({ get }) => { const daysToAdd = get(myAtom); const returnDate = new Date(date); returnDate.setDate(returnDate.getDate() + daysToAdd); return returnDate; } }); set(myAtom, 1); // $FlowFixMe[incompatible-call] added when improving typing for this parameters expect(getValue(mySelector(new Date(2021, 2, 25))).getDate()).toBe(26); set(myAtom, 2); // $FlowFixMe[incompatible-call] added when improving typing for this parameters expect(getValue(mySelector(new Date(2021, 2, 25))).getDate()).toBe(27); }); testRecoil('Works with supersets', () => { const mySelector = selectorFamily({ key: 'selectorFamily/supersets', get: ({ multiplier }) => ({ get }) => get(myAtom) * multiplier }); set(myAtom, 1); expect(getValue(mySelector({ multiplier: 10 }))).toBe(10); expect(getValue(mySelector({ multiplier: 100 }))).toBe(100); expect(getValue(mySelector({ multiplier: 100, extra: 'foo' }))).toBe(100); }); testRecoil('selectorFamily - writable', () => { const mySelector = selectorFamily({ key: 'selectorFamily/writable', get: ({ multiplier }) => ({ get }) => get(myAtom) * multiplier, set: ({ multiplier }) => ({ set }, num) => set(myAtom, num instanceof DefaultValue ? num : num / multiplier) }); set(myAtom, 1); expect(getValue(mySelector({ multiplier: 10 }))).toBe(10); set(mySelector({ multiplier: 10 }), 20); expect(getValue(myAtom)).toBe(2); set(mySelector({ multiplier: 10 }), 30); expect(getValue(myAtom)).toBe(3); set(mySelector({ multiplier: 100 }), 400); expect(getValue(myAtom)).toBe(4); }); testRecoil('selectorFamily - value caching', () => { let evals = 0; const mySelector = selectorFamily({ key: 'selectorFamily/value caching', get: ({ multiplier }) => ({ get }) => { evals++; return get(myAtom) * multiplier; } }); expect(evals).toBe(0); set(myAtom, 1); expect(getValue(mySelector({ multiplier: 10 }))).toBe(10); expect(evals).toBe(1); expect(getValue(mySelector({ multiplier: 10 }))).toBe(10); expect(evals).toBe(1); expect(getValue(mySelector({ multiplier: 100 }))).toBe(100); expect(evals).toBe(2); expect(getValue(mySelector({ multiplier: 100 }))).toBe(100); expect(evals).toBe(2); expect(getValue(mySelector({ multiplier: 10 }))).toBe(10); expect(evals).toBe(2); set(myAtom, 2); expect(getValue(mySelector({ multiplier: 10 }))).toBe(20); expect(evals).toBe(3); expect(getValue(mySelector({ multiplier: 10 }))).toBe(20); expect(evals).toBe(3); expect(getValue(mySelector({ multiplier: 100 }))).toBe(200); expect(evals).toBe(4); expect(getValue(mySelector({ multiplier: 100 }))).toBe(200); expect(evals).toBe(4); }); testRecoil('selectorFamily - reference caching', () => { let evals = 0; const mySelector = selectorFamily({ key: 'selectorFamily/reference caching', get: ({ multiplier }) => ({ get }) => { evals++; return get(myAtom) * multiplier; }, cachePolicyForParams_UNSTABLE: { equality: 'reference' } }); expect(evals).toBe(0); set(myAtom, 1); expect(getValue(mySelector({ multiplier: 10 }))).toBe(10); expect(evals).toBe(1); expect(getValue(mySelector({ multiplier: 10 }))).toBe(10); expect(evals).toBe(2); expect(getValue(mySelector({ multiplier: 100 }))).toBe(100); expect(evals).toBe(3); expect(getValue(mySelector({ multiplier: 100 }))).toBe(100); expect(evals).toBe(4); expect(getValue(mySelector({ multiplier: 10 }))).toBe(10); expect(evals).toBe(5); set(myAtom, 2); expect(getValue(mySelector({ multiplier: 10 }))).toBe(20); expect(evals).toBe(6); expect(getValue(mySelector({ multiplier: 10 }))).toBe(20); expect(evals).toBe(7); expect(getValue(mySelector({ multiplier: 100 }))).toBe(200); expect(evals).toBe(8); expect(getValue(mySelector({ multiplier: 100 }))).toBe(200); expect(evals).toBe(9); const multiply10 = { multiplier: 10 }; const multiply100 = { multiplier: 100 }; set(myAtom, 1); expect(getValue(mySelector(multiply10))).toBe(10); expect(evals).toBe(10); expect(getValue(mySelector(multiply10))).toBe(10); expect(evals).toBe(10); expect(getValue(mySelector(multiply100))).toBe(100); expect(evals).toBe(11); expect(getValue(mySelector(multiply100))).toBe(100); expect(evals).toBe(11); expect(getValue(mySelector(multiply10))).toBe(10); expect(evals).toBe(11); set(myAtom, 2); expect(getValue(mySelector(multiply10))).toBe(20); expect(evals).toBe(12); expect(getValue(mySelector(multiply10))).toBe(20); expect(evals).toBe(12); expect(getValue(mySelector(multiply100))).toBe(200); expect(evals).toBe(13); expect(getValue(mySelector(multiply100))).toBe(200); expect(evals).toBe(13); }); // Parameterized selector results should be frozen unless // dangerouslyAllowMutability is set testRecoil('selectorFamily - mutability', () => { const myImmutableSelector = selectorFamily({ key: 'selectorFamily/immutable', get: ({ key }) => ({ get }) => ({ [key]: get(myAtom) }) }); set(myAtom, 42); const immutableResult: { [string]: number, ... } = getValue(myImmutableSelector({ key: 'foo' })); expect(immutableResult).toEqual({ foo: 42 }); expect(() => { immutableResult.foo = 2600; }).toThrow(); const myMutableSelector = selectorFamily({ key: 'selectorFamily/mutable', get: ({ key }) => ({ get }) => ({ [key]: get(myAtom) }), dangerouslyAllowMutability: true }); set(myAtom, 42); const mutableResult: { [string]: number, ... } = getValue(myMutableSelector({ key: 'foo' })); expect(mutableResult).toEqual({ foo: 42 }); mutableResult.foo = 2600; expect(mutableResult).toEqual({ foo: 2600 }); }); testRecoil('selectorFamily - evaluate to RecoilValue', () => { const atomA = atom({ key: 'selectorFamily/const atom A', default: 'A' }); const atomB = atom({ key: 'selectorFamily/const atom B', default: 'B' }); const mySelector = selectorFamily<string, string>({ key: 'selectorFamily/', get: param => () => param === 'a' ? atomA : atomB }); expect(getValue(mySelector('a'))).toEqual('A'); expect(getValue(mySelector('b'))).toEqual('B'); });