
Попробуем соединить Redux и GraphQL без использования Apollo Client или Relay.
Что такое Redux
Редакс это архитектурный подход, который реализован в виде небольшой библиотеки. Он предполагает, что у приложения есть хранилище данных state. Изменять хранилище state напрямую нельзя. Это можно сделать только через вспомогательные функции reducers.
Псевдокод:
function dispatch(action) { state = reducer(state, action); }
Для соединения реакта и редакса используется библиотека react-redux. Она позволяет получать данные из state внутри компонентов и инициировать их обновление через dispatch.
Что такое GraphQL
GraphQL для клиента это просто POST-запрос с особым форматом body:
|
REST |
GraphQL |
|---|---|
|
GET /books/id |
POST /graphql |
|
POST /books |
POST /graphql |
|
UPDATE /books/id |
POST /graphql |
|
DELETE /books/id |
POST /graphql |
GraphQL без Apollo Client (и Relay)
Apollo-client это самая популярная библиотека для работы с GraphQL. Она предоставляет интерфейс для работы с запросами внутри компонентов, а также решает задачу хранения кэша. Вместо Redux предполагается использовать внутренние механизмы. Однако эти механизмы далеко не такие простые и удобные.
Вместо Apollo Client для примера воспользуемся такой функцией:
const graphqlAPI = (query, variables) => fetch("/graphql", { method: "POST", body: JSON.stringify({ query, variables }) });
redux-toolkit
Я буду использовать официальный набор утилит от redux — redux-toolkit. Они решают самую частую претензию к редаксу — бесконечное количество шаблонного кода. В этот набор входит:
-
createEntityAdapter для упрощения работы с редьюсерами и селекторами.
-
createAsyncThunk для упрощения работы с сайд-эффектами и API.
-
createSlice для упрощения работы с actions и разрешения мутабельности.
Пример
Представим простое приложение, которое запрашивает список книг с сервера и выводит их.
// booksSlice.js import { createEntityAdapter, createAsyncThunk, createSlice } from "@reduxjs/toolkit"; // Наш редьюсер будет хранить список книг. // createEntityAdapter генерирует готовый набор функций // для добавления, обновления и удаления книг внутри редьюсера // (addOne, addMany, updateOne, removeOne, setAll, ...), const books = createEntityAdapter(); // createAsyncThunk позволяет работать с асинхронным кодом. // Например, с запросами к API. // getBooks работает как набор actions, которые можно диспатчить в приложении. export const getBooks = createAsyncThunk("get books", async () => await graphqlAPI(` query { books { id title } } `) ); export const addBook = createAsyncThunk("add book", ({ title }) => graphqlAPI(` mutation ($title: string!){ add_book(objects: { title: $title }) { id title } } `, { title }) ); // createSlice — обертка над createAction и createReducer. // Она позволяет работать со стейтом мутабильно. export const booksSlice = createSlice({ name: "books", initialState: books.getInitialState(), extraReducers: { // Работаем с результатом запроса getBooks [getBooks.fulfilled]: (state, action) => { // setAll автоматически создан в createEntityAdapter books.setAll(state, action.payload); }, // Работаем с результатом запроса addBook [addBook.fulfilled]: (state, action) => { // addOne автоматически создан в createEntityAdapter books.addOne(state, action.payload); }, }, }); // createEntityAdapter генерирует набор готовых селекторов // (selectIds, selectById, selectAll, ...) export const booksSelectors = books.getSelectors((s) => s.books); export default booksSlice.reducer;
// index.js import { useDispatch, useSelector } from "react-redux"; import { getBooks, addBook, booksSelectors } from "./booksSlice"; export function Books() { const dispatch = useDispatch(); // booksSelectors.selectAll втоматически создан через createEntityAdapter const books = useSelector(booksSelectors.selectAll); useEffect(() => { dispatch(getBooks()); }, [dispatch]); return ( <div> <button onClick={() => dispatch(addBook({ title: "New Book" }))}> Add book </button> <ul> {books.map((b) => <li key={b.id}>{b.title}</li>)} </ul> </div> ); }
Что дальше
Поэкспериментировать с Redux можно локально.
Для настройки окружения достаточно одной команды:npx create-react-app my-app --template redux
Для Typescript:npx create-react-app my-app --template redux-typescript
Вместо fetch можно воспользоваться библиотекой graphql-request.
Создать бэкенд на GraphQL можно без написания кода.
Для этого подходят такие проекты как hasura или prisma.
ссылка на оригинал статьи https://habr.com/ru/post/524000/
Добавить комментарий