Reducer — часть глобального состояние приложения, в котором описывается как состояние будет изменяться. У reducer есть действия, c помощью которых, приложение, меняет свое состояние. Типичный reducer в redux представляет собой файл с импортом констант или action creators, начальным состоянием reducer, функциями-помощниками, и самим reducer.

Например:

//books.reducer.js
import { ADD_BOOK, DELETE_BOOK, EDIT_BOOK, GET_BOOKS } from 'constants/actionTypes/book';
import mockedBooks from 'mocks/books';

const initState = {
  books: [],
};

function addBook(booksList, book) {
  return [...booksList, book];
}

function deleteBook(booksList, bookID) {
  return booksList.filter((book) => book.id !== bookID);
}

function editBook(booksList, editedBook) {
  return booksList.map((book) => {
    if (book.id === editedBook.id) {
      return { ...book, ...editedBook };
    }
    return book;
  });
}

export function booksReducer(state = initState, action) {
  switch (action.type) {
    case GET_BOOKS: {
      return {
        books: mockedBooks,
      };
    }
    case ADD_BOOK: {
      return {
        ...state,
        books: addBook(state.books, action.payload.book),
      };
    }
    case DELETE_BOOK: {
      return {
        ...state,
        books: deleteBook(state.books, action.payload.id),
      };
    }
    case EDIT_BOOK: {
      return {
        ...state,
        books: editBook(state.books, action.payload.book),
      };
    }
    default: {
      return state;
    }
  }
}

При замене redux на effector возникает вопрос переписывание одних сущностей на другие. Проведем аналогию:

Модель в Effector

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

Перепишем reducer на model

// books.model.js
import { createEffect, createStore, createEvent } from 'effector';
import mockedBooks from 'mocks/books';

function handleAddBook(books, book) {
  return [...books, book];
}

function handleDeleteBook(books, bookID) {
  return books.filter((book) => book.id !== bookID);
}

function handleEditBook(books, editedBook) {
  return books.map((book) => {
    if (book.id === editedBook.id) {
      return { ...book, ...editedBook };
    }
    return book;
  });
}

function noop() {}

/**
 * Это модель - файл описывающий сущности, реакции, и связи между ними.
 * Ивент описывается как "что произошло", and returns a new state value.
 * A reducer's function signature is: (state, action) => newState
 *
 * The Redux state should contain only plain JS objects, arrays, and primitives.
 * The root state value is usually an object.  It's important that you should
 * not mutate the state object, but return a new object if the state changes.
 *
 * You can use any conditional logic you want in a reducer. In this example,
 * we use a switch statement, but it's not required.
 */

export const $books = createStore([]);

const fxGetBooks = createEffect({ handler: () => Promise.resolve(mockedBooks) });

export const getBooks = fxGetBooks.prepend(noop);
export const editedBook = createEvent();
export const addedBook = createEvent();
export const deletedBook = createEvent();

$books
  .on(fxGetBooks.doneData, (_, books) => books)
  .on(editBook, handleEditBook)
  .on(addBook, handleAddBook)
  .on(deleteBook, handleDeleteBook);
booksReducer меняем на создание Store, где в вызове функции createStore
аргументом задаем начальное состояние (default state).
- const initState = { books: [] };
- export function booksReducer(state = initState, action) {}

+ export const $books = createStore([])

Action type меняем на Event.
- export const ADD_BOOK = 'ADD_BOOK';
+ export const addBook = createEvent();

Switch case меняем на .on
- switch (action.type) {
-   case ADD_BOOK: {
-      return {
-        ...state,
-        books: addBook(state.books, action.payload.book),
-      };
-    }

+$books.on(addBook, handleAddBook)