Docs
Selectors

Selectors

Selectors provide a way to create derived states. They can be created with the same atom function. There is one difference: selectors should have a get method that will run whenever any of the states they are subscribed to changes.

Advantages

  • Selectors are re-rendered only when any of the atom's they are subscribed to updates their state.
  • They can return promises
  • They are re-calculated even when they are not rendered in a component or hook (note that you will need to render them using the useValue or useAtom hook if you want them to run the most up-to-date get function). This ensures they have the most up-to-date calculated value before they are rendered. This also makes them a good solution for background syncing, because they can get other atom's values without subscribing to their updates by using the getValue function
import { atom, useAtom, useValue } from 'atomic-state'
 
const todosState = atom({
  key: 'todos',
  default: []
})
 
const completedTodosState = atom({
  key: 'completedTodos',
  get({ get }) {
    // This selector will re-render only when 'todosState' changes.
    // You can check that by adding a console.log :)
 
    const todos = get(todosState)
 
    return todos.filter((todo) => todo.completed)
  }
})
 
function CompletedTodos() {
  // Type is inferred here
  const completedTodos = useValue(completedTodosState)
 
  return <p>Completed todos: {todos.length}</p>
}
💡

Because selectors depend on other atoms' states, their state cannot be set manually. This also means that actions don't work inside them

They can also return promises:

@/states/index.jsx
import { atom } from 'atomic-state'
 
const textState = atom({
  key: 'text',
  default: ''
})
 
const searchResultsState = atom({
  key: 'searchResults',
  default: [],
  async get({ get }) {
    const text = get(textState)
 
    return fetch('/search?q=' + text).then((res) => res.json())
  }
})
💡

Note that in this case the result of the selector is a Promise, so it will not be available while it's resolving. In that case, default is required. The return type will be inferred from it as well.

They can also subscribe to other selectors:

import { atom } from 'atomic-state'
 
import { searchResultsState } from '@/states'
 
const infoState = atom({
  key: 'info',
  default: {
    name: '',
    searchResults: []
  },
  async get({ get }) {
    const searchResults = await get(searchResultsState)
 
    return {
      name: '',
      searchResults
    }
  }
})

In this example, we are using async/await because the current state of searchResultsState could still be a Promise

Last updated on October 9, 2023