Skip to content

Instantly share code, notes, and snippets.

@Akiyamka
Last active June 28, 2023 08:31
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Akiyamka/d87df58821cc38958360f02a82494ab6 to your computer and use it in GitHub Desktop.
Save Akiyamka/d87df58821cc38958360f02a82494ab6 to your computer and use it in GitHub Desktop.
reatom 2 - Обзор с примерами

Асинхронный эффект в атоме

Разберем как сделать асинхронный запрос в апи для получения курса валюты. На react c useEffect это выглядит так:

import { useEffect, useState } from 'react';

Я не буду говорить о преимуществах reatom перед другими популярными решениями - этому вопросу уже посвящен отдельный раздел официальной документации. Вместо этого я попытаюсь разобратся как им пользоватся сравнивая с примерами на react хуках.

Счетчик

Пусть у нас есть следующие события: INCREMENT - увеличивает счетчик на один DECREMENT - уменьшает счетчик на один SET - устанавливает счетчик в определенное значение RESET - сбрасывает счетчик на 0

С использованием useReducer это будет выглядеть так:

import { useReducer } from 'react';

const initialState = {count: 0};

function reducer(state, action) {
  switch (action.type) {
    case 'INCREMENT':
      return {count: state.count + 1};
    case 'DECREMENT':
      return {count: state.count - 1};
    case 'SET':
      return action.payload;
    case 'RESET':
      return 0;
    default:
      throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({type: 'DECREMENT'})}>-</button>
      <button onClick={() => dispatch({type: 'INCREMENT'})}>+</button>
      <button onClick={() => dispatch({type: 'SET', payload: 10 })}>10</button>
      <button onClick={() => dispatch({type: 'RESET'})}>Reset</button>
    </>
  );
}

Перепишем на reatom (здесь и далее речь идет о @reatom/react@2.0.0-rc5):

Первое что мы делаем создаем "atom" -

Что еще за атом? Атом это самостоятельная частичка состояния приложения. Атомы могут следить за изменениями других атомов, или событий в приложении. Чаще всего они описывают какую-то самостоятельную сущность приложения. Оперирировать атомами намного удобнее чем большим монолитным стейтом. Они гормонично продолжают концепцию компонентного подхода, только на уровне менеджмента состояний и сайд эффектов.

У нас был очень простой стейт с всего одним полем - count - так что нам хватит одного атома countAtom.

Атом создается с помощью библиотечной функции createAtom(dependencies, reducer, options): где dependencies - это объект который описывает на что должен реагировать этот атом. По смыслу это очень похоже на второй аргумент useEffect (dependency array). Однако, у атома в качестве зависимостей атома могут быть события (dispatch), или другие атомы. Сейчас нас интересует только реакция на события и описываетя это вот так:

import { createAtom } from '@reatom/core';

createAtom(
  {
    INCREMENT: () => {},
    DECREMENT: () => {},
    SET: (value) => value,
    RESET: () => {},
  },
  ...
)

Ключ обьекта - под каким именем будет доступна зависимость, а переданная в качестве значения функция выполняет роль адаптера - здесь мы можем выполнить какую-то подготовку, например - извлечь значение из события:

  {
    onChange: (event) => event.currentTarget.value,
  }

reducer, второй аргумент createAtom функции, описывает сайдэфекты. В отличии от reducer в redux, его возможности не ограничиваются генерацией нового состояния, но в нашем примере нам нужно только менять состояние:

({ onAction }, state = initialState) => {
   onAction('INCREMENT', () => (state = state + 1));
   onAction('DECREMENT', () => (state = state - 1));
   onAction('SET', (value => (state = value));
   onAction('RESET', () => (state = initialState));
}

Для того чтобы использовать атом в компоненте реакта будет удобно воспользоватся эфектом useAtom из библиотеки @reatom/react

Итого поулчается следующее:

import { createAtom } from '@reatom/core';
import '@reatom/react/react-dom-batched-updates'; // Для react 16 версии и выше
import { useAtom } from '@reatom/react';

const initialState = 0;

const counterAtom = createAtom(
  {
    INCREMENT: () => {},
    DECREMENT: () => {},
    SET: (value) => value,
    RESET: () => {},
  },
  ({ onAction }, state = initialState) => {
    onAction('INCREMENT', () => (state = state + 1));
    onAction('DECREMENT', () => (state = state - 1));
    onAction('SET', (value => (state = value));
    onAction('RESET', () => (state = initialState));
  }
);

function Counter() {
  const [count, { increment, decrement, set, reset }] = useAtom(counterAtom);
  return (
    <>
      Count: {count}
      <button onClick={() => increment()}>-</button>
      <button onClick={() => decrement())}>+</button>
      <button onClick={() => set(10)}>10</button>
      <button onClick={() => reset())}>Reset</button>
    </>
  );
}

P.S. Дальше мы будем отталкиваться от примера выше, хотя в данном случае можно было бы записать еще короче используя встроенный хелпер createPrimitiveAtom:

import { createPrimitiveAtom } from '@reatom/core/primitives';

const initialState = 0;

const counterAtom = createPrimitiveAtom(
  initialState,
  {
    INCREMENT: (state) => state + 1,
    DECREMENT: (state) => state - 1,
    SET: (state, value) => value,
    RESET: () => initialState,
  }
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment