Skip to content

Instantly share code, notes, and snippets.

@jacques-blom
Created April 27, 2021 13:12
Show Gist options
  • Save jacques-blom/23c3514d43b1f5223d291245bf5abc32 to your computer and use it in GitHub Desktop.
Save jacques-blom/23c3514d43b1f5223d291245bf5abc32 to your computer and use it in GitHub Desktop.
Atom Effects 3
import {Button} from '@chakra-ui/button'
import {Input} from '@chakra-ui/input'
import {Box, Divider, Heading, VStack} from '@chakra-ui/layout'
import React, {useState} from 'react'
import {
atom,
atomFamily,
DefaultValue,
useRecoilCallback,
useRecoilState,
useRecoilValue,
useResetRecoilState,
} from 'recoil'
import {shoppingListAPI} from './fakeAPI'
type ItemType = {
label: string
checked: boolean
}
const idsState = atom<number[]>({
key: 'ids',
default: [],
effects_UNSTABLE: [
() => {
// TODO: Fetch a list of item ids from the server
},
],
})
const itemState = atomFamily<ItemType, number>({
key: 'item',
default: {label: '', checked: false},
effects_UNSTABLE: (id) => [
() => {
// TODO:
// 1. Fetch individual item data from the API and initialise the atoms
// 2. Update/create individual item data via the API
},
],
})
export const AtomEffects = () => {
const ids = useRecoilValue(idsState)
const resetList = useResetRecoilState(idsState)
const nextId = ids.length
const insertItem = useRecoilCallback(({set}) => (label: string) => {
set(idsState, [...ids, nextId])
set(itemState(nextId), {label, checked: false})
})
return (
<Container onClear={() => resetList()}>
{ids.map((id) => (
<Item key={id} id={id} />
))}
<NewItemInput
onInsert={(label) => {
insertItem(label)
}}
/>
</Container>
)
}
const Container: React.FC<{onClear: () => void}> = ({children, onClear}) => {
return (
<Box display="flex" flexDir="column" alignItems="center" pt={10}>
<Box width="400px" backgroundColor="yellow.100" p={5} borderRadius="lg">
<Heading size="lg" mb={4}>
Shopping List
</Heading>
<VStack spacing={3} divider={<Divider borderColor="rgba(86, 0, 0, 0.48)" />}>
{children}
</VStack>
</Box>
<Button variant="link" mt={3} onClick={onClear}>
Clear list
</Button>
</Box>
)
}
type ItemProps = {
id: number
}
const Item = ({id}: ItemProps) => {
const [item, setItem] = useRecoilState(itemState(id))
return (
<Box
rounded="md"
textDecoration={item.checked ? 'line-through' : ''}
opacity={item.checked ? 0.5 : 1}
_hover={{textDecoration: 'line-through'}}
cursor="pointer"
width="100%"
onClick={() => setItem({...item, checked: !item.checked})}
>
{item.label}
</Box>
)
}
const NewItemInput = ({onInsert}: {onInsert: (label: string) => void}) => {
const [value, setValue] = useState('')
return (
<Input
value={value}
placeholder="New item"
padding={0}
height="auto"
border="none"
_focus={{border: 'none'}}
_placeholder={{color: 'rgba(86, 0, 0, 0.48)'}}
onChange={(e) => {
setValue(e.currentTarget.value)
}}
onKeyPress={({key}) => {
if (key === 'Enter') {
onInsert(value)
setValue('')
}
}}
/>
)
}
const randomDelay = () => {
return new Promise<void>((resolve) => {
setTimeout(resolve, randomIntBetween(500, 3000))
})
}
export const getWeather = async (zipCode: string) => {
await randomDelay()
if (!getWeatherCache[zipCode]) {
getWeatherCache[zipCode] = randomIntBetween(5, 35)
} else {
getWeatherCache[zipCode] += randomIntBetween(-1, 2)
}
return getWeatherCache[zipCode]
}
const getWeatherCache: Record<string, number> = {}
function randomIntBetween(min: number, max: number) {
return Math.floor(Math.random() * (max - min + 1) + min)
}
type ItemType = {
label: string
checked: boolean
}
class ShoppingListAPI {
items: Record<number, ItemType>
constructor() {
const persisted = localStorage.getItem('ShoppingListAPI')
if (persisted == null) {
this.items = {}
} else {
this.items = JSON.parse(persisted)
}
}
async getItems() {
await this.randomDelay('getItems')
return this.items
}
async getItem(id: number): Promise<ItemType | undefined> {
await this.randomDelay('getItem', id)
return this.items[id]
}
async createOrUpdateItem(id: number, item: ItemType) {
await this.randomDelay('createOrUpdateItem', id)
this.items[id] = item
this.persist()
}
async deleteItem(id: number) {
await this.randomDelay('deleteItem', id)
delete this.items[id]
this.persist()
}
private async randomDelay(name: string, param?: number) {
let label = `Fake Request: ${name}.`
if (param != null) label += ` id: ${param}`
await randomDelay()
console.log(`End ${label}`)
}
private persist() {
localStorage.setItem('ShoppingListAPI', JSON.stringify(this.items))
}
}
export const shoppingListAPI = new ShoppingListAPI()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment