Чим Effector відрізняється від Redux?
Effector і Redux - це обидва менеджери стану, але вони побудовані на зовсім різних філософіях. Якщо коротко:
Effector - реактивна, декларативна система; Redux - імперативна, редьюсерна архітектура.
Розберемо по пунктах і з прикладами.
1. Підхід до керування станом
| Effector | Redux / RTK | |
|---|---|---|
| Парадигма | Реактивна | Імперативна |
| Як описується логіка | "Що залежить від чого" (dataflow) | "Що робити, коли сталася подія" (reducers) |
| Оновлення | Автоматичні за залежностями | Ручні через dispatch() |
| Філософія | Опиши потік даних | Опиши, як reducer змінює store |
Effector:
const increment = createEvent()
const $count = createStore(0).on(increment, c => c + 1)Redux:
const increment = () => ({ type: 'increment' })
const reducer = (state = 0, action) =>
action.type === 'increment' ? state + 1 : state2. Архітектура і принципи
| Effector | Redux | |
|---|---|---|
| Стан | Декілька незалежних store | Один великий store (або slices) |
| Сайд-ефекти | Через createEffect() (вбудовано) | Через thunk, saga, observable тощо |
| Підписки | Реактивні (.watch, .map, sample) | Через useSelector() / middleware |
| Типізація | Відмінна інтеграція з TypeScript | Краще з Redux Toolkit, але все одно громіздко |
| Продуктивність | Мінімальні ререндери, fine-grained реактивність | Може викликати зайві ререндери |
| SSR | Із коробки | Потрібне ручне керування станом |
3. Робота з асинхронністю
Effector вбудовано керує асинхронними ефектами:
const fetchUserFx = createEffect(async id => {
const res = await fetch(`/api/users/${id}`)
return res.json()
})
fetchUserFx.done.watch(({ result }) => console.log('User', result))У Redux потрібно вручну писати middleware:
const fetchUser = id => async dispatch => {
dispatch({ type: 'pending' })
const res = await fetch(`/api/users/${id}`)
dispatch({ type: 'success', payload: await res.json() })
}Effector робить це декларативно, без middleware.
4. Гнучкість і масштабування
Effector дозволяє описувати залежності явно:
sample({
source: $user,
clock: updateClicked,
fn: (user) => ({ ...user, updated: true }),
target: $user,
})У Redux подібний реактивний зв'язок неможливий - потрібно вручну диспатчити ланцюжок дій.
Effector не вимагає глобального стора:
ти можеш створити локальний store для будь-якого компонента або модуля.
Redux же передбачає єдину централізовану структуру (store -> reducer tree).
5. Продуктивність і підписки
Effector підписує компонент тільки на потрібні дані.
При зміні інших store компонент не ререндериться.
Redux, навіть з useSelector, часто викликає зайві ререндери, якщо не використовувати мемоізацію (shallowEqual, reselect тощо).
6. Код і DX (Developer Experience)
Effector:
const addTodo = createEvent<string>()
const $todos = createStore<string[]>([]).on(addTodo, (list, todo) => [...list, todo])Redux Toolkit:
const todosSlice = createSlice({
name: 'todos',
initialState: [],
reducers: {
addTodo: (state, action) => { state.push(action.payload) }
}
})Effector - менше шаблонного коду, більше декларативності і реактивних зв'язків. Redux Toolkit - більш імперативний, але знайомий розробникам зі старих екосистем.
7. Коли обрати що
| Сценарій | Що обрати |
|---|---|
| Маленький застосунок | Zustand, Jotai, Effector |
| Великий SPA з бізнес-логікою | Effector |
| Enterprise / Legacy / знайома команда | Redux Toolkit |
| Потрібен строгий dataflow і реактивність | Effector |
| Потрібен зрозумілий "по кроках" флоу | Redux Toolkit |
8. Коротко однією фразою
Effector - це реактивний dataflow-менеджер: описуєш зв'язки, і все оновлюється саме. Redux - це імперативний action-reducer-менеджер: викликаєш дії, все оновлюється вручну.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.