Skip to content

Commit

Permalink
feat: sync jotai renders with useState renders (pmndrs#827) (pmndrs#841)
Browse files Browse the repository at this point in the history
* feat: test case

* a workaround for useReducer weird behavior

* fix a test for react18

* fix bail out check

Co-authored-by: daishi <[email protected]>
  • Loading branch information
Aslemammad and dai-shi authored Nov 24, 2021
1 parent 2c0791a commit f315f87
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 11 deletions.
24 changes: 23 additions & 1 deletion src/core/useAtom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,29 @@ export function useAtom<Value, Update, Result extends void | Promise<void>>(
throw new Error('no atom value')
}, [store, atom])

const [value, forceUpdate] = useReducer(getAtomValue, undefined, getAtomValue)
const [[value, atomFromUseReducer], forceUpdate] = useReducer(
useCallback(
(prev) => {
const nextValue = getAtomValue()
if (Object.is(prev[0], nextValue) && prev[1] === atom) {
return prev // bail out
}
return [nextValue, atom]
},
[getAtomValue, atom]
),
undefined,
() => {
const initialValue = getAtomValue()
return [initialValue, atom]
}
)

if (atomFromUseReducer !== atom) {
// Note: This seems like a useReducer bug, doesn't it?
// https://github.com/pmndrs/jotai/issues/827
forceUpdate()
}

useEffect(() => {
const unsubscribe = store[SUBSCRIBE_ATOM](atom, forceUpdate)
Expand Down
46 changes: 36 additions & 10 deletions tests/basic.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -788,18 +788,10 @@ it('changes atom from parent (#273, #275)', async () => {
await findByText('commits: 1, id: a')

fireEvent.click(getByText('atom b'))
if (IS_REACT18) {
await findByText('commits: 3, id: b')
} else {
await findByText('commits: 2, id: b')
}
await findByText('commits: 2, id: b')

fireEvent.click(getByText('atom a'))
if (IS_REACT18) {
await findByText('commits: 5, id: a')
} else {
await findByText('commits: 3, id: a')
}
await findByText('commits: 3, id: a')
})

it('should be able to use a double derived atom twice and useEffect (#373)', async () => {
Expand Down Expand Up @@ -896,3 +888,37 @@ it('async chain for multiple sync and async atoms (#443)', async () => {
await findByText('loading')
await findByText('count: 3')
})

it('sync re-renders with useState re-renders (#827)', async () => {
const atom0 = atom('atom0')
const atom1 = atom('atom1')
const atom2 = atom('atom2')
const atoms = [atom0, atom1, atom2]

const App = () => {
const [currentAtomIndex, setCurrentAtomIndex] = useState(0)
const rotateAtoms = () => {
setCurrentAtomIndex((prev) => (prev + 1) % atoms.length)
}
const [atomValue] = useAtom(atoms[currentAtomIndex] as typeof atoms[number])

return (
<>
<span>commits: {useCommitCount()}</span>
<h1>{atomValue}</h1>
<button onClick={rotateAtoms}>rotate</button>
</>
)
}
const { findByText, getByText } = render(
<Provider>
<App />
</Provider>
)

await findByText('commits: 1')
fireEvent.click(getByText('rotate'))
await findByText('commits: 2')
fireEvent.click(getByText('rotate'))
await findByText('commits: 3')
})

0 comments on commit f315f87

Please sign in to comment.