Разберем как сделать асинхронный запрос в апи для получения курса валюты. На react c useEffect это выглядит так:
import { useEffect, useState } from 'react';
Я не буду говорить о преимуществах reatom перед другими популярными решениями - этому вопросу уже посвящен отдельный раздел официальной документации. Вместо этого я попытаюсь разобратся как им пользоватся сравнивая с примерами на react хуках.
Пусть у нас есть следующие события:
INCREMENT
- увеличивает счетчик на один
DECREMENT
- уменьшает счетчик на один
SET
- устанавливает счетчик в определенное значение
RESET
- сбрасывает счетчик на 0
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>
</>
);
}
Первое что мы делаем создаем "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,
}
);