Skip to content

Commit

Permalink
attempt 2: refactor with store hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
dmaskasky committed Jan 11, 2025
1 parent 0d23697 commit ece5b0b
Show file tree
Hide file tree
Showing 7 changed files with 937 additions and 178 deletions.
26 changes: 5 additions & 21 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,7 @@
"plugin:import/errors",
"plugin:import/warnings"
],
"plugins": [
"@typescript-eslint",
"react",
"prettier",
"react-hooks",
"import",
"jest"
],
"plugins": ["@typescript-eslint", "react", "prettier", "react-hooks", "import", "jest"],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 2018,
Expand All @@ -39,6 +32,7 @@
"import/no-unresolved": ["error", { "commonjs": true, "amd": true }],
"import/export": "error",
"import/no-duplicates": ["error"],
"prettier/prettier": ["error", { "printWidth": 100 }],
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/no-unused-vars": [
"warn",
Expand All @@ -47,23 +41,13 @@
"@typescript-eslint/no-use-before-define": "off",
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/no-explicit-any": "off",
"jest/consistent-test-it": [
"error",
{ "fn": "it", "withinDescribe": "it" }
],
"@typescript-eslint/no-unused-expressions": "off",
"jest/consistent-test-it": ["error", { "fn": "it", "withinDescribe": "it" }],
"import/order": [
"error",
{
"alphabetize": { "order": "asc", "caseInsensitive": true },
"groups": [
"builtin",
"external",
"internal",
"parent",
"sibling",
"index",
"object"
],
"groups": ["builtin", "external", "internal", "parent", "sibling", "index", "object"],
"newlines-between": "never",
"pathGroups": [
{
Expand Down
2 changes: 1 addition & 1 deletion .prettierrc
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
"singleQuote": true,
"bracketSameLine": true,
"tabWidth": 2,
"printWidth": 80
"printWidth": 100
}
90 changes: 44 additions & 46 deletions __tests__/atomEffect.test.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,23 @@
import React, { createElement, useEffect } from 'react'
import { act, render, renderHook, waitFor } from '@testing-library/react'
import { Provider, useAtom, useAtomValue, useSetAtom } from 'jotai/react'
import { atom, createStore, getDefaultStore } from 'jotai/vanilla'
import { atom } from 'jotai/vanilla'
import { atomEffect } from '../src/atomEffect'
import {
ErrorBoundary,
assert,
delay,
increment,
incrementLetter,
} from './test-utils'
import { createStore } from './store'
import { ErrorBoundary, assert, delay, increment, incrementLetter } from './test-utils'

it('should run the effect on vanilla store', () => {
const store = createStore().unstable_derive(
(getAtomState, setAtomState, ...rest) => [
getAtomState,
(atom, atomState) =>
Object.assign(setAtomState(atom, atomState), {
const store = createStore().unstable_derive((getAtomState, setAtomState, ...rest) => [
getAtomState,
(atom, atomState) =>
setAtomState(
atom,
Object.assign(atomState, {
label: atom.debugLabel,
}),
...rest,
]
)
})
),
...rest,
])
const countAtom = atom(0)
countAtom.debugLabel = 'count'
const effectAtom = atomEffect((_, set) => {
Expand Down Expand Up @@ -85,6 +81,7 @@ it('should run the effect on mount and cleanup on unmount and whenever countAtom
})

let didMount = false
const store = createStore()
function useTest() {
const [count, setCount] = useAtom(countAtom)
useAtomValue(effectAtom)
Expand All @@ -93,7 +90,10 @@ it('should run the effect on mount and cleanup on unmount and whenever countAtom
}, [count])
return setCount
}
const { result, rerender, unmount } = renderHook(useTest)
const wrapper = ({ children }: { children: React.ReactNode }) => (
<Provider store={store as any}>{children}</Provider>
)
const { result, rerender, unmount } = renderHook(useTest, { wrapper })
function incrementCount() {
const setCount = result.current
setCount(increment)
Expand Down Expand Up @@ -142,7 +142,7 @@ it('should not cause infinite loops when effect updates the watched atom', () =>
runCount++
set(watchedAtom, increment)
})
const store = getDefaultStore()
const store = createStore()
store.sub(effectAtom, () => void 0)

const incrementWatched = () => store.set(watchedAtom, increment)
Expand All @@ -164,7 +164,7 @@ it('should not cause infinite loops when effect updates the watched atom asynchr
set(watchedAtom, increment)
}, 0)
})
const store = getDefaultStore()
const store = createStore()
store.sub(effectAtom, () => void 0)
// changing the value should run the effect again one time
store.set(watchedAtom, increment)
Expand All @@ -183,7 +183,7 @@ it('should allow synchronous recursion with set.recurse for first run', () => {
}
recurse(watchedAtom, increment)
})
const store = getDefaultStore()
const store = createStore()
store.sub(effectAtom, () => void 0)
expect({ runCount, watched: store.get(watchedAtom) }).toEqual({
runCount: 4, // 2
Expand All @@ -206,7 +206,7 @@ it('should allow synchronous recursion with set.recurse', () => {
}
recurse(watchedAtom, increment)
})
const store = getDefaultStore()
const store = createStore()
store.sub(effectAtom, () => void 0)
store.set(watchedAtom, increment)
expect(store.get(watchedAtom)).toBe(5)
Expand All @@ -229,7 +229,7 @@ it('should allow multiple synchronous recursion with set.recurse', () => {
recurse(watchedAtom, increment)
recurse(watchedAtom, increment)
})
const store = getDefaultStore()
const store = createStore()
store.sub(effectAtom, () => void 0)
store.set(watchedAtom, increment)
expect({ runCount, value: store.get(watchedAtom) }).toEqual({
Expand Down Expand Up @@ -266,7 +266,7 @@ it('should batch updates during synchronous recursion with set.recurse', () => {
])
set.recurse(updateAtom)
})
const store = getDefaultStore()
const store = createStore()
store.sub(effectAtom, () => void 0)
store.set(watchedAtom, increment)
expect(store.get(lettersAndNumbersAtom)).toEqual(['a0', 'b1'])
Expand All @@ -289,7 +289,7 @@ it('should allow asynchronous recursion with task delay with set.recurse', async
recurse(watchedAtom, increment)
})
})
const store = getDefaultStore()
const store = createStore()
store.sub(effectAtom, () => void 0)
await waitFor(() => assert(done))
expect(store.get(watchedAtom)).toBe(3)
Expand All @@ -311,7 +311,7 @@ it('should allow asynchronous recursion with microtask delay with set.recurse',
recurse(watchedAtom, increment)
})
})
const store = getDefaultStore()
const store = createStore()
store.sub(effectAtom, () => void 0)
await waitFor(() => assert(store.get(watchedAtom) >= 3))
expect(store.get(watchedAtom)).toBe(3)
Expand All @@ -321,23 +321,26 @@ it('should allow asynchronous recursion with microtask delay with set.recurse',
it('should work with both set.recurse and set', () => {
expect.assertions(3)
let runCount = 0
const watchedAtom = atom(0)
const valueAtom = atom(0)
const countAtom = atom(0)
const effectAtom = atomEffect((get, set) => {
const value = get(watchedAtom)
const value = get(valueAtom)
if (value >= 5) {
throw new Error()
}
get(countAtom)
runCount++
if (value === 0 || value % 3) {
set.recurse(watchedAtom, increment)
set.recurse(valueAtom, increment)
set(countAtom, increment)
return
}
set(watchedAtom, increment)
set(valueAtom, increment)
})
const store = getDefaultStore()
const store = createStore()
store.sub(effectAtom, () => void 0)
expect(store.get(countAtom)).toBe(3)
expect(store.get(watchedAtom)).toBe(4)
expect(store.get(valueAtom)).toBe(4)
expect(runCount).toBe(4)
})

Expand All @@ -354,7 +357,7 @@ it('should disallow synchronous set.recurse in cleanup', () => {
})
return cleanup
})
const store = getDefaultStore()
const store = createStore()
store.sub(effectAtom, () => void 0)
store.set(anotherAtom, increment)
expect(() => store.set(anotherAtom, increment)).toThrowError(
Expand All @@ -380,7 +383,7 @@ it('should return value from set.recurse', () => {
return
}
})
const store = getDefaultStore()
const store = createStore()
store.sub(effectAtom, () => void 0)
expect(results).toEqual([1, 2, 3, 4, 5])
})
Expand Down Expand Up @@ -654,7 +657,7 @@ it('should batch synchronous updates as a single transaction', () => {
letters + String(numbers),
])
})
const store = getDefaultStore()
const store = createStore()
store.sub(effectAtom, () => void 0)

expect(runCount).toBe(1)
Expand Down Expand Up @@ -722,11 +725,9 @@ it('should abort the previous promise', async () => {
const completedRuns: number[] = []
const resolves: (() => void)[] = []
const countAtom = atom(0)
const abortControllerAtom = atom<{ abortController: AbortController | null }>(
{
abortController: null,
}
)
const abortControllerAtom = atom<{ abortController: AbortController | null }>({
abortController: null,
})
const effectAtom = atomEffect((get) => {
const currentRun = runCount++
get(countAtom)
Expand Down Expand Up @@ -818,7 +819,7 @@ it('should not infinite loop with nested atomEffects', async () => {
get(readOnlyAtom)
})

const store = getDefaultStore()
const store = createStore()
store.sub(effect2Atom, () => void 0)

await waitFor(() => assert(delayedIncrement))
Expand Down Expand Up @@ -851,7 +852,7 @@ it('should not rerun with get.peek', () => {
get.peek(countAtom)
runCount++
})
const store = getDefaultStore()
const store = createStore()
store.sub(effectAtom, () => void 0)
store.set(countAtom, increment)
expect(runCount).toBe(1)
Expand All @@ -869,10 +870,7 @@ it('should trigger the error boundary when an error is thrown', async () => {
let didThrow = false
function wrapper() {
return (
<ErrorBoundary
componentDidCatch={() => (didThrow = true)}
children={<TestComponent />}
/>
<ErrorBoundary componentDidCatch={() => (didThrow = true)} children={<TestComponent />} />
)
}
const originalConsoleError = console.error
Expand Down
Loading

0 comments on commit ece5b0b

Please sign in to comment.