Skip to content

Commit

Permalink
Merge branch 'main' into broadcast-channel
Browse files Browse the repository at this point in the history
  • Loading branch information
AjaxSolutions authored Sep 2, 2023
2 parents b5115b8 + ccba918 commit b8c2372
Show file tree
Hide file tree
Showing 9 changed files with 98 additions and 21 deletions.
4 changes: 1 addition & 3 deletions docs/recipes/atom-with-broadcast.mdx
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
---
title: atomWithBroadcast
nav: 8.08
nav: 8.09
keywords: creators,broadcast
---

## atomWithBroadcast

> `atomWithBroadcast` creates an atom. The atom will be shared between
> browser tabs and frames, similar to `atomWithStorage` but with the
> initialization limitation.
Expand Down
2 changes: 0 additions & 2 deletions docs/recipes/atom-with-compare.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ nav: 8.05
keywords: creators,compare
---

## atomWithCompare

> `atomWithCompare` creates atom that triggers updates when custom compare function `areEqual(prev, next)` is false.
This can help you avoid unwanted re-renders by ignoring state changes that don't matter to your application.
Expand Down
4 changes: 1 addition & 3 deletions docs/recipes/atom-with-debounce.mdx
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
---
title: atomWithDebounce
nav: 8.09
nav: 8.10
keywords: creators,debounce
---

## atomWithDebounce

> `atomWithDebounce` helps with creating an atom where state set should be debounced.
This util is useful for text search inputs, where you would like to call **functions in derived atoms only once** after waiting for a duration, instead of firing an action on every keystroke.
Expand Down
4 changes: 1 addition & 3 deletions docs/recipes/atom-with-listeners.mdx
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
---
title: atomWithListeners
nav: 8.07
nav: 8.08
keywords: creators,listeners
---

## atomWithListeners

> `atomWithListeners` creates an atom and a hook. The hook can be called to
> add a new listener. The hook takes as an argument a callback, and that
> callback is called every time the atom's value is set. The hook also
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
---
title: atomWithRefreshAndDefault
nav: 8.06
nav: 8.07
keywords: creators,refresh,default
---

## atomWithRefreshAndDefault

> This is for an another implementation of [atomWithDefault](../utilities/resettable.mdx#atomwithdefault)
> This is for another implementation of [atomWithDefault](../utilities/resettable.mdx#atomwithdefault)
### Look back to atomWithDefault behavior

Expand Down
62 changes: 62 additions & 0 deletions docs/recipes/atom-with-refresh.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
---
title: atomWithRefresh
nav: 8.06
keywords: creators,refresh
---

> `atomWithRefresh` creates a derived atom that can be force-refreshed, by using
> the update function.
This is helpful when you need to refresh asynchronous data after performing a
side effect.

It can also be used to implement "pull to refresh" functionality.

```ts
import { atom, Getter } from 'jotai'

export function atomWithRefresh<T>(fn: (get: Getter) => T) {
const refreshCounter = atom(0)

return atom(
(get) => {
get(refreshCounter)
return fn(get)
},
(_, set) => set(refreshCounter, (i) => i + 1)
)
}
```

Here's how you'd use it to implement an refresh-able source of data:

```js
import { atomWithRefresh } from 'XXX'

const postsAtom = atomWithRefresh((get) =>
fetch('https://jsonplaceholder.typicode.com/posts').then((r) => r.json())
)
```

In a component:

```jsx
const PostsList = () => {
const [posts, refreshPosts] = useAtom(postsAtom)

return (
<div>
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>

{/* Clicking this button will re-fetch the posts */}
<button type="button" onClick={refreshPosts}>
Refresh posts
</button>
</div>
)
}
```
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
---
title: atomWithToggleAndStorage
nav: 8.04
keywords: creators,storage,toggle
keywords: creators,storage
---

## atomWithToggleAndStorage

> `atomWithToggleAndStorage` is like `atomWithToggle` but also persist the state anytime it changes in given storage using [`atomWithStorage`](../utilities/storage.mdx).
Here is the source:
Expand Down
2 changes: 0 additions & 2 deletions docs/recipes/atom-with-toggle.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ nav: 8.03
keywords: creators,toggle
---

## atomWithToggle

> `atomWithToggle` creates a new atom with a boolean as initial state & a setter function to toggle it.
This avoids the boilerplate of having to set up another atom just to update the state of the first.
Expand Down
31 changes: 30 additions & 1 deletion tests/react/vanilla-utils/atomWithObservable.test.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Component, StrictMode, Suspense, useState } from 'react'
import type { ReactElement, ReactNode } from 'react'
import { act, fireEvent, render, waitFor } from '@testing-library/react'
import { BehaviorSubject, Observable, Subject, delay, of } from 'rxjs'
import { BehaviorSubject, Observable, Subject, delay, map, of } from 'rxjs'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { fromValue, makeSubject, pipe, toObservable } from 'wonka'
import { useAtom, useAtomValue, useSetAtom } from 'jotai/react'
Expand Down Expand Up @@ -795,4 +795,33 @@ describe('atomWithObservable vanilla tests', () => {

unsub()
})

it('can propagate updates with rxjs chains', async () => {
const store = createStore()

const single$ = new Subject<number>()
const double$ = single$.pipe(map((n) => n * 2))

const singleAtom = atomWithObservable(() => single$)
const doubleAtom = atomWithObservable(() => double$)

const unsubs = [
store.sub(singleAtom, () => {}),
store.sub(doubleAtom, () => {}),
]

single$.next(1)
expect(store.get(singleAtom)).toBe(1)
expect(store.get(doubleAtom)).toBe(2)

single$.next(2)
expect(store.get(singleAtom)).toBe(2)
expect(store.get(doubleAtom)).toBe(4)

single$.next(3)
expect(store.get(singleAtom)).toBe(3)
expect(store.get(doubleAtom)).toBe(6)

unsubs.forEach((unsub) => unsub())
})
})

0 comments on commit b8c2372

Please sign in to comment.