Actions
Action is a base Reatom primitive that increases the quality of your code in many ways: organization and readability, debugability, extensibility.
The beauty of Reatom is that you don’t need to use actions for simple updates, like (value) => myAtom.set(value). Actions are useful for complex operations, like data mappings, API calls and other side effects.
You can call actions anywhere just like regular functions. You can describe its parameters just like with regular functions. You can type your action function with TypeScript generics as usual. action itself is a simple decorator which adds some extra features to your function, but does not limit you in any way.
import { atom, action } from '@reatom/core'
export const list = atom([])const isListLoading = atom(false)
const loadList = action(async (page: number) => { isListLoading.set(true) try { const response = await fetch(`/api/list?page=${page}`) const payload = await response.json() list.set(payload) } finally { isListLoading.set(false) }})
loadList(1) // PromiseNote that action is an optional feature and not required in your code, but it is always nice to use it.
Naming
Section titled “Naming”Most Reatom units accept an optional name for debugging purposes. We highly recommend using it, as it helps to debug the runtime dataflow.
export const list = atom([], 'list')const isListLoading = atom(false, 'isListLoading')
const loadList = action(async (page: number) => { // ...}, 'loadList')That’s better!
Extend
Section titled “Extend”Under the hood action is a special type of atom; it gives us the ability to reuse many patterns and extensions. In the next chapter, we will get to know extensions more closely, but for now, let’s learn how to better organize our code.
extend accept a callback with the processed target, which return an object to assign to the target.
import { atom, action } from '@reatom/core'
export const list = atom([], 'list').extend((target) => { const isLoading = atom( false, // compute the name from the target `${target.name}.isLoading`, ) const load = action(async (page: number) => { // ... }, `${target.name}.load`)
// return things that you want to assign to the current atom return { isLoading, load, }})Now you can access your states in a clean and readable way:
import React from 'react'import { reatomComponent } from '@reatom/react'import { list } from './model'
const Paging = reatomComponent(() => { const [page, setPage] = React.useState(1)
React.useEffect(() => { list.load(page) }, [page])
const isLoading = list.isLoading()
return ( <button onClick={() => setPage((page) => page + 1)} disabled={isLoading}> {isLoading ? 'Loading...' : 'Next page'} </button> )})
const List = reatomComponent(() => ( <section> <Paging /> {list().map(/* ... */)} </section>))Awesome, now you can couple relative states with relative components without a props drilling!
But this is just the beginning, .extend can give us much more! Check out the next section to learn more about it.