deno.land / x / jotai@v1.8.4 / tests / utils / atomFamily.test.tsx
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291import { StrictMode, Suspense, useState } from 'react'import { fireEvent, render, waitFor } from '@testing-library/react'import { atom, useAtom, useSetAtom } from 'jotai'import type { SetStateAction, WritableAtom } from 'jotai'import { atomFamily } from 'jotai/utils'import { getTestProvider } from '../testUtils'
const Provider = getTestProvider()
it('new atomFamily impl', async () => { const myFamily = atomFamily((param: string) => atom(param))
const Displayer = ({ index }: { index: string }) => { const [count] = useAtom(myFamily(index)) return <div>count: {count}</div> } const { findByText } = render( <StrictMode> <Provider> <Displayer index={'a'} /> </Provider> </StrictMode> )
await findByText('count: a')})
it('primitive atomFamily returns same reference for same parameters', async () => { const myFamily = atomFamily((num: number) => atom({ num })) expect(myFamily(0)).toEqual(myFamily(0)) expect(myFamily(0)).not.toEqual(myFamily(1)) expect(myFamily(1)).not.toEqual(myFamily(0))})
it('read-only derived atomFamily returns same reference for same parameters', async () => { const arrayAtom = atom([0]) const myFamily = atomFamily((num: number) => atom((get) => get(arrayAtom)[num] as number) ) expect(myFamily(0)).toEqual(myFamily(0)) expect(myFamily(0)).not.toEqual(myFamily(1)) expect(myFamily(1)).not.toEqual(myFamily(0))})
it('removed atom creates a new reference', async () => { const bigAtom = atom([0]) const myFamily = atomFamily((num: number) => atom((get) => get(bigAtom)[num] as number) )
const savedReference = myFamily(0)
expect(savedReference).toEqual(myFamily(0))
myFamily.remove(0)
const newReference = myFamily(0)
expect(savedReference).not.toEqual(newReference)
myFamily.remove(1337)
expect(myFamily(0)).toEqual(newReference)})
it('primitive atomFamily initialized with props', async () => { const myFamily = atomFamily((param: number) => atom(param))
const Displayer = ({ index }: { index: number }) => { const [count, setCount] = useAtom(myFamily(index)) return ( <div> count: {count} <button onClick={() => setCount((c) => c + 10)}>button</button> </div> ) }
const Parent = () => { const [index, setIndex] = useState(1)
return ( <div> <button onClick={() => setIndex((i) => i + 1)}>increment</button> <Displayer index={index} /> </div> ) }
const { findByText, getByText } = render( <StrictMode> <Provider> <Parent /> </Provider> </StrictMode> )
await findByText('count: 1')
fireEvent.click(getByText('button')) await findByText('count: 11')
fireEvent.click(getByText('increment')) await findByText('count: 2')
fireEvent.click(getByText('button')) await findByText('count: 12')})
it('derived atomFamily functionality as usual', async () => { const arrayAtom = atom([0, 0, 0])
const myFamily = atomFamily((param: number) => atom( (get) => get(arrayAtom)[param] as number, (_, set, update) => { set(arrayAtom, (oldArray) => { if (typeof oldArray[param] === 'undefined') return oldArray
const newValue = typeof update === 'function' ? update(oldArray[param] as number) : update
const newArray = [ ...oldArray.slice(0, param), newValue, ...oldArray.slice(param + 1), ]
return newArray }) } ) )
const Displayer = ({ index, countAtom, }: { index: number countAtom: WritableAtom<number, SetStateAction<number>> }) => { const [count, setCount] = useAtom(countAtom) return ( <div> index: {index}, count: {count} <button onClick={() => setCount((oldValue) => oldValue + 1)}> increment #{index} </button> </div> ) }
const indicesAtom = atom((get) => [...new Array(get(arrayAtom).length)])
const Parent = () => { const [indices] = useAtom(indicesAtom)
return ( <div> {indices.map((_, index) => ( <Displayer key={index} index={index} countAtom={myFamily(index)} /> ))} </div> ) }
const { getByText } = render( <StrictMode> <Provider> <Parent /> </Provider> </StrictMode> )
await waitFor(() => { getByText('index: 0, count: 0') getByText('index: 1, count: 0') getByText('index: 2, count: 0') })
fireEvent.click(getByText('increment #1')) await waitFor(() => { getByText('index: 0, count: 0') getByText('index: 1, count: 1') getByText('index: 2, count: 0') })
fireEvent.click(getByText('increment #0')) await waitFor(() => { getByText('index: 0, count: 1') getByText('index: 1, count: 1') getByText('index: 2, count: 0') })
fireEvent.click(getByText('increment #2')) await waitFor(() => { getByText('index: 0, count: 1') getByText('index: 1, count: 1') getByText('index: 2, count: 1') })})
it('custom equality function work', async () => { const bigAtom = atom([0])
const badFamily = atomFamily((num: { index: number }) => atom((get) => get(bigAtom)[num.index] as number) )
const goodFamily = atomFamily( (num: { index: number }) => atom((get) => get(bigAtom)[num.index] as number), (l, r) => l.index === r.index )
expect(badFamily({ index: 0 })).not.toEqual(badFamily({ index: 0 })) expect(badFamily({ index: 0 })).not.toEqual(badFamily({ index: 0 }))
expect(goodFamily({ index: 0 })).toEqual(goodFamily({ index: 0 })) expect(goodFamily({ index: 0 })).not.toEqual(goodFamily({ index: 1 }))})
it('a derived atom from an async atomFamily (#351)', async () => { const countAtom = atom(1) const getAsyncAtom = atomFamily((n: number) => atom(async () => { await new Promise((r) => setTimeout(r, 100)) return n + 10 }) ) const derivedAtom = atom((get) => get(getAsyncAtom(get(countAtom))))
const Counter = () => { const setCount = useSetAtom(countAtom) const [derived] = useAtom(derivedAtom) return ( <> <div>derived: {derived}</div> <button onClick={() => setCount((c) => c + 1)}>button</button> </> ) }
const { getByText, findByText } = render( <StrictMode> <Provider> <Suspense fallback="loading"> <Counter /> </Suspense> </Provider> </StrictMode> )
await findByText('loading') await findByText('derived: 11')
await new Promise((r) => setTimeout(r, 100)) fireEvent.click(getByText('button')) await findByText('loading') await findByText('derived: 12')
await new Promise((r) => setTimeout(r, 100)) fireEvent.click(getByText('button')) await findByText('loading') await findByText('derived: 13')})
it('setShouldRemove with custom equality function', async () => { const myFamily = atomFamily( (num: { index: number }) => atom(num), (l, r) => l.index === r.index ) let firstTime = true myFamily.setShouldRemove(() => { if (firstTime) { firstTime = false return true } return false })
const family1 = myFamily({ index: 0 }) const family2 = myFamily({ index: 0 }) const family3 = myFamily({ index: 0 })
expect(family1).not.toBe(family2) expect(family2).toBe(family3)})
Version Info