From 30c9971f200707f30cc1fb72b35967ed142ca97b Mon Sep 17 00:00:00 2001 From: daishi Date: Sun, 20 Jun 2021 11:09:30 +0900 Subject: [PATCH 1/4] support refresh in atomWithDefault --- src/utils.ts | 2 +- src/utils/atomWithDefault.ts | 30 ++++++++++++++++++------------ 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/utils.ts b/src/utils.ts index e816d08c63..373f228336 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -9,7 +9,7 @@ export { selectAtom } from './utils/selectAtom' export { useAtomCallback } from './utils/useAtomCallback' export { freezeAtom, freezeAtomCreator } from './utils/freezeAtom' export { splitAtom } from './utils/splitAtom' -export { atomWithDefault } from './utils/atomWithDefault' +export { atomWithDefault, REFRESH } from './utils/atomWithDefault' export { waitForAll } from './utils/waitForAll' export { atomWithHash } from './utils/atomWithHash' export { atomWithStorage } from './utils/atomWithStorage' diff --git a/src/utils/atomWithDefault.ts b/src/utils/atomWithDefault.ts index c2ef84698f..008133cd98 100644 --- a/src/utils/atomWithDefault.ts +++ b/src/utils/atomWithDefault.ts @@ -1,14 +1,15 @@ import { atom } from 'jotai' -import type { Atom, PrimitiveAtom } from 'jotai' +import type { Atom, WritableAtom, SetStateAction } from 'jotai' type Read = Atom['read'] -export function atomWithDefault( - getDefault: Read -): PrimitiveAtom { +export const REFRESH = Symbol() + +export function atomWithDefault(getDefault: Read) { + type Update = SetStateAction | typeof REFRESH const EMPTY = Symbol() const overwrittenAtom = atom(EMPTY) - const anAtom: PrimitiveAtom = atom( + const anAtom: WritableAtom = atom( (get) => { const overwritten = get(overwrittenAtom) if (overwritten !== EMPTY) { @@ -16,13 +17,18 @@ export function atomWithDefault( } return getDefault(get) }, - (get, set, update) => - set( - overwrittenAtom, - typeof update === 'function' - ? (update as (prev: Value) => Value)(get(anAtom)) - : update - ) + (get, set, update) => { + if (update === REFRESH) { + set(overwrittenAtom, EMPTY) + } else { + set( + overwrittenAtom, + typeof update === 'function' + ? (update as (prev: Value) => Value)(get(anAtom)) + : update + ) + } + } ) return anAtom } From 25be8862a98e7f3fef9bbf638341f35a397258b6 Mon Sep 17 00:00:00 2001 From: daishi Date: Sun, 20 Jun 2021 11:09:30 +0900 Subject: [PATCH 2/4] support refresh in atomWithDefault --- src/utils.ts | 2 +- src/utils/atomWithDefault.ts | 30 ++++++++++++++++++------------ 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/utils.ts b/src/utils.ts index e816d08c63..373f228336 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -9,7 +9,7 @@ export { selectAtom } from './utils/selectAtom' export { useAtomCallback } from './utils/useAtomCallback' export { freezeAtom, freezeAtomCreator } from './utils/freezeAtom' export { splitAtom } from './utils/splitAtom' -export { atomWithDefault } from './utils/atomWithDefault' +export { atomWithDefault, REFRESH } from './utils/atomWithDefault' export { waitForAll } from './utils/waitForAll' export { atomWithHash } from './utils/atomWithHash' export { atomWithStorage } from './utils/atomWithStorage' diff --git a/src/utils/atomWithDefault.ts b/src/utils/atomWithDefault.ts index c2ef84698f..008133cd98 100644 --- a/src/utils/atomWithDefault.ts +++ b/src/utils/atomWithDefault.ts @@ -1,14 +1,15 @@ import { atom } from 'jotai' -import type { Atom, PrimitiveAtom } from 'jotai' +import type { Atom, WritableAtom, SetStateAction } from 'jotai' type Read = Atom['read'] -export function atomWithDefault( - getDefault: Read -): PrimitiveAtom { +export const REFRESH = Symbol() + +export function atomWithDefault(getDefault: Read) { + type Update = SetStateAction | typeof REFRESH const EMPTY = Symbol() const overwrittenAtom = atom(EMPTY) - const anAtom: PrimitiveAtom = atom( + const anAtom: WritableAtom = atom( (get) => { const overwritten = get(overwrittenAtom) if (overwritten !== EMPTY) { @@ -16,13 +17,18 @@ export function atomWithDefault( } return getDefault(get) }, - (get, set, update) => - set( - overwrittenAtom, - typeof update === 'function' - ? (update as (prev: Value) => Value)(get(anAtom)) - : update - ) + (get, set, update) => { + if (update === REFRESH) { + set(overwrittenAtom, EMPTY) + } else { + set( + overwrittenAtom, + typeof update === 'function' + ? (update as (prev: Value) => Value)(get(anAtom)) + : update + ) + } + } ) return anAtom } From f071c16eeb8a10c9aacd761c9597402ef0c25a80 Mon Sep 17 00:00:00 2001 From: Luca Colonnello Date: Tue, 22 Jun 2021 23:31:17 +0100 Subject: [PATCH 3/4] Continuation of fix(utils/atomWithDefault): support refresh (#545) * support refresh in atomWithDefault * test: tests for atomWithDefault refresh feature * chore: update size-snapshot.json Co-authored-by: daishi --- .size-snapshot.json | 14 ++-- tests/utils/atomWithDefault.test.tsx | 95 +++++++++++++++++++++++++++- 2 files changed, 101 insertions(+), 8 deletions(-) diff --git a/.size-snapshot.json b/.size-snapshot.json index 929b9eea29..d3e7d683a1 100644 --- a/.size-snapshot.json +++ b/.size-snapshot.json @@ -14,16 +14,16 @@ } }, "utils.js": { - "bundled": 12627, - "minified": 6183, - "gzipped": 2303, + "bundled": 12759, + "minified": 6230, + "gzipped": 2323, "treeshaked": { "rollup": { "code": 28, "import_statements": 28 }, "webpack": { - "code": 1280 + "code": 1289 } } }, @@ -70,9 +70,9 @@ } }, "query.js": { - "bundled": 2114, - "minified": 1005, - "gzipped": 535, + "bundled": 2129, + "minified": 1020, + "gzipped": 539, "treeshaked": { "rollup": { "code": 84, diff --git a/tests/utils/atomWithDefault.test.tsx b/tests/utils/atomWithDefault.test.tsx index 0b57f081c5..4f0193e240 100644 --- a/tests/utils/atomWithDefault.test.tsx +++ b/tests/utils/atomWithDefault.test.tsx @@ -1,7 +1,7 @@ import React, { Suspense } from 'react' import { fireEvent, render } from '@testing-library/react' import { atom, useAtom } from '../../src/index' -import { atomWithDefault } from '../../src/utils' +import { atomWithDefault, REFRESH } from '../../src/utils' import { getTestProvider } from '../testUtils' const Provider = getTestProvider() @@ -84,3 +84,96 @@ it('simple async get default', async () => { fireEvent.click(getByText('button1')) await findByText('count1: 3, count2: 5') }) + +it('refresh sync atoms to default values', async () => { + const count1Atom = atom(1) + const count2Atom = atomWithDefault((get) => get(count1Atom) * 2) + + const Counter: React.FC = () => { + const [count1, setCount1] = useAtom(count1Atom) + const [count2, setCount2] = useAtom(count2Atom) + return ( + <> +
+ count1: {count1}, count2: {count2} +
+ + + + + ) + } + + const { findByText, getByText } = render( + + + + ) + + await findByText('count1: 1, count2: 2') + + fireEvent.click(getByText('button1')) + await findByText('count1: 2, count2: 4') + + fireEvent.click(getByText('button2')) + await findByText('count1: 2, count2: 5') + + fireEvent.click(getByText('button1')) + await findByText('count1: 3, count2: 5') + + fireEvent.click(getByText('Refresh count2')) + await findByText('count1: 3, count2: 6') + + fireEvent.click(getByText('button1')) + await findByText('count1: 4, count2: 8') +}) + +it('refresh async atoms to default values', async () => { + const count1Atom = atom(1) + const count2Atom = atomWithDefault(async (get) => { + await new Promise((r) => setTimeout(r, 10)) + return get(count1Atom) * 2 + }) + + const Counter: React.FC = () => { + const [count1, setCount1] = useAtom(count1Atom) + const [count2, setCount2] = useAtom(count2Atom) + return ( + <> +
+ count1: {count1}, count2: {count2} +
+ + + + + ) + } + + const { findByText, getByText } = render( + + + + + + ) + + await findByText('loading') + await findByText('count1: 1, count2: 2') + + fireEvent.click(getByText('button1')) + await findByText('loading') + await findByText('count1: 2, count2: 4') + + fireEvent.click(getByText('button2')) + await findByText('count1: 2, count2: 5') + + fireEvent.click(getByText('button1')) + await findByText('count1: 3, count2: 5') + + fireEvent.click(getByText('Refresh count2')) + await findByText('count1: 3, count2: 6') + + fireEvent.click(getByText('button1')) + await findByText('count1: 4, count2: 8') +}) From 7863f3d64838882cbc1a2253548d52c1b9dc5e5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathis=20M=C3=B8ller?= Date: Wed, 23 Jun 2021 13:26:01 +0200 Subject: [PATCH 4/4] Fix fake timers --- .size-snapshot.json | 12 ++++++------ package.json | 2 +- tests/utils/waitForAll.test.tsx | 6 +++--- yarn.lock | 20 ++++++++++---------- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/.size-snapshot.json b/.size-snapshot.json index d3e7d683a1..464abf4522 100644 --- a/.size-snapshot.json +++ b/.size-snapshot.json @@ -1,8 +1,8 @@ { "index.js": { - "bundled": 20786, - "minified": 9964, - "gzipped": 3254, + "bundled": 20821, + "minified": 9995, + "gzipped": 3260, "treeshaked": { "rollup": { "code": 14, @@ -14,9 +14,9 @@ } }, "utils.js": { - "bundled": 12759, - "minified": 6230, - "gzipped": 2323, + "bundled": 12933, + "minified": 6369, + "gzipped": 2409, "treeshaked": { "rollup": { "code": 28, diff --git a/package.json b/package.json index 7a2ba31f9e..bd399af659 100644 --- a/package.json +++ b/package.json @@ -162,7 +162,7 @@ "@rollup/plugin-babel": "^5.3.0", "@rollup/plugin-node-resolve": "^13.0.0", "@rollup/plugin-typescript": "^8.2.1", - "@testing-library/react": "^11.2.7", + "@testing-library/react": "^12.0.0", "@types/jest": "^26.0.23", "@types/react": "^17.0.11", "@types/react-dom": "^17.0.7", diff --git a/tests/utils/waitForAll.test.tsx b/tests/utils/waitForAll.test.tsx index 81e4dcfd75..4b0934785e 100644 --- a/tests/utils/waitForAll.test.tsx +++ b/tests/utils/waitForAll.test.tsx @@ -9,17 +9,17 @@ const Provider = getTestProvider() const consoleWarn = console.warn const consoleError = console.error beforeEach(() => { + jest.useFakeTimers() console.warn = jest.fn() console.error = jest.fn() }) afterEach(() => { + jest.runOnlyPendingTimers() + jest.useRealTimers() console.warn = consoleWarn console.error = consoleError }) -// FIXME this seems to break in jest 27 -// jest.useFakeTimers() - class ErrorBoundary extends React.Component< { message?: string }, { hasError: boolean } diff --git a/yarn.lock b/yarn.lock index 9ebf7ad1e0..340651ab59 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1269,10 +1269,10 @@ dependencies: "@sinonjs/commons" "^1.7.0" -"@testing-library/dom@^7.28.1": - version "7.31.2" - resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-7.31.2.tgz#df361db38f5212b88555068ab8119f5d841a8c4a" - integrity sha512-3UqjCpey6HiTZT92vODYLPxTBWlM8ZOOjr3LX5F37/VRipW2M1kX6I/Cm4VXzteZqfGfagg8yXywpcOgQBlNsQ== +"@testing-library/dom@^8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.0.0.tgz#2bb994393c566aae021db86dd263ba06e8b71b38" + integrity sha512-Ym375MTOpfszlagRnTMO+FOfTt6gRrWiDOWmEnWLu9OvwCPOWtK6i5pBHmZ07wUJiQ7wWz0t8+ZBK2wFo2tlew== dependencies: "@babel/code-frame" "^7.10.4" "@babel/runtime" "^7.12.5" @@ -1281,15 +1281,15 @@ chalk "^4.1.0" dom-accessibility-api "^0.5.6" lz-string "^1.4.4" - pretty-format "^26.6.2" + pretty-format "^27.0.2" -"@testing-library/react@^11.2.7": - version "11.2.7" - resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-11.2.7.tgz#b29e2e95c6765c815786c0bc1d5aed9cb2bf7818" - integrity sha512-tzRNp7pzd5QmbtXNG/mhdcl7Awfu/Iz1RaVHY75zTdOkmHCuzMhRL83gWHSgOAcjS3CCbyfwUHMZgRJb4kAfpA== +"@testing-library/react@^12.0.0": + version "12.0.0" + resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-12.0.0.tgz#9aeb2264521522ab9b68f519eaf15136148f164a" + integrity sha512-sh3jhFgEshFyJ/0IxGltRhwZv2kFKfJ3fN1vTZ6hhMXzz9ZbbcTgmDYM4e+zJv+oiVKKEWZPyqPAh4MQBI65gA== dependencies: "@babel/runtime" "^7.12.5" - "@testing-library/dom" "^7.28.1" + "@testing-library/dom" "^8.0.0" "@tootallnate/once@1": version "1.1.2"