Минуту назад кто-то задал мне вопрос о том, как он может использовать Redux и Next.js. Что ж, разработчиков пугает redux из-за его шаблонного кода еще до того, как вы запустите его и запустите. Но на самом деле это действительно классный инструмент, облегчающий жизнь любому пользователю Redux.
Сегодня я покажу вам Redux Toolkit с простым примером CRUD с использованием Next.js. Неважно, знакомы ли вы с Redux или только начали работать с Redux, этот пост для вас.

Что такое редукс-инструментарий?

Redux Toolkit — это самоуверенный набор инструментов с батарейками для эффективной разработки Redux. Он поставляется с наиболее широко используемыми надстройками Redux из коробки, такими как Redux Thunk для асинхронной логики и Reselect для написания функций селектора, так что вы можете использовать их сразу, не устанавливая их отдельно.

Хватит разговоров, давайте напишем код.

Во-первых, давайте начнем с создания базового проекта Next.js Typescript с помощью:

npx create-next-app redux-toolkit-example --ts

Корень вашего проекта будет выглядеть так:

Приступим к созданию пользовательского интерфейса.

Перейдите на страницу pages/index.js и замените код по умолчанию следующим:

export default function Home() {
  return (
    <div className="conatiner">
      <div className="list-container">
        <div className="list-header">
          <h1 className="title">
            Lists<span>.</span>
          </h1>
          <div className="input-field">
            <input type="text" className="search" placeholder="Search..." />
            <button className="btn">Search</button>
          </div>
        </div>
        <div className="list-body">
          <div className="list-item">
            <div className="list-item-content">milk</div>
            <button className="list-item-footer">X</button>
          </div>
          <div className="list-item">
            <div className="list-item-content">sugar</div>
            <button className="list-item-footer">X</button>
          </div>
          <div className="list-item">
            <div className="list-item-content">coffee</div>
            <button className="list-item-footer">X</button>
          </div>
          <div className="list-item">
            <div className="list-item-content">eggs</div>
            <button className="list-item-footer">X</button>
          </div>
        </div>
      </div>
    </div>
  );
}

Затем перейдите в styles/global.css и замените код следующим образом:

html,
body {
  padding: 0;
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
    Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
  background-color: #fafafa;
}
a {
  color: inherit;
  text-decoration: none;
}
* {
  box-sizing: border-box;
}
.conatiner {
  max-width: 700px;
  margin: 0 auto;
}
.list-container {
  display: flex;
  justify-self: center;
  align-self: center;
  flex-direction: column;
  width: 500px;
}
.list-header {
  width: 100%;
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.list-header .title {
  font-size: 2rem;
}
.list-header .input-field input {
  margin-right: 1em;
  padding: 8px 10px;
  border-radius: 10px;
  border: #ccc solid 1px;
}
.list-header .input-field button {
  display: inline-block;
  background-color: #78f0f0;
  color: #000;
  border-radius: 10px;
  border: none;
  padding: 8px 10px;
  cursor: pointer;
}
span {
  color: #78f0f0;
}
.list-body {
  width: 100%;
  margin-top: 2em;
}
.list-item {
  display: flex;
  justify-content: space-between;
  margin-bottom: 1em;
}
.list-item button {
  background-color: #78f0f0;
  color: #000;
  border-radius: 50%;
  border: none;
  padding: 8px 10px;
  cursor: pointer;
}

Теперь запустите проект с помощью yarn или npm, в зависимости от используемого менеджера пакетов, в моем случае это npm.

npm run dev

Это запустит сервер на localhost:3000, затем откройте localhost:3000 в браузере, и вы увидите это напечатанным на веб-странице:

Мы закончили создание пользовательского интерфейса.

Давайте углубимся в redux-toolkit

Начнем с установки необходимых пакетов:

npm i @reduxjs/toolkit react-redux

Когда это будет сделано, создайте новую папку в корневом каталоге с именем store, затем создайте два файла config.js и rootReducer.js в /магазин.

rootReducer.js

является корнем всех редюсеров.
вставьте этот код в rootReducer.js:

import { combineReducers } from "@reduxjs/toolkit";
import { listSlice } from "./ducks/list";
const rootReducer = combineReducers({
  list: listSlice.reducer,
});
export default rootReducer;

Здесь произошло то, что я импортировал функцию combineReducers() из @reduxjs/toolkit. Вспомогательная функция combineReducers превращает объект, значения которого являются разными редукционными функциями, в единую редукционную функцию, которую вы можете передать в createStore. У нас будет один редуктор, поэтому combineReducers не нужен. Но по мере того, как ваше приложение будет становиться все более сложным, вы захотите разделить функцию сокращения на отдельные функции.
А также импортировать listSlice, которые мы еще не создали.

config.js

config.js — это место, где мы настраиваем наш набор инструментов для работы с редукцией.
вставьте этот код в config.js:

import { configureStore } from "@reduxjs/toolkit";
import rootReducer from "./rootReducer";
const store = configureStore({
  reducer: rootReducer,
});
export type AppDispatch = typeof store.dispatch;
export type AppThunk = ThunkAction<void, RootState, unknown, Action>;
export default store;

Теперь мы настраиваем хранилище с помощью функции configureStore. configureStore — это дружественная абстракция стандартной функции Redux createStore, которая добавляет хорошие значения по умолчанию в хранилище, настроенное для лучшего опыта разработки. эта функция автоматически настроит расширение redux devtools, вы также можете передать дополнительную конфигурацию функции. чтобы узнать больше, обратитесь к документам.

Ломтики

Создайте вызов каталога ducks с файлом listSlice.js в нем.
Вставьте его в listSlice.js:

import { createSlice } from "@reduxjs/toolkit";
export type listState = {
  list: any[];
};
const initialState: listState = {
  list: ["egg", "milk", "sugar", "coffee"],
};
export const listSlice: any = createSlice({
  name: "list",
  initialState,
  reducers: {
    addList: (state, { payload }) => void state.list.push(payload),
    removeList: (state, { payload }) =>
      void state.list.splice(state.list.findIndex((item) => item === payload)),
  },
  extraReducers: {},
});
export const { addList, removeList } = listSlice.actions;
export const listSelector = (state: any) => state.list;

Хорошо, теперь мы создаем наш первый фрагмент с помощью функции createSlice, которая выполняет два действия, и у нас есть исходное состояние list с некоторыми элементами по умолчанию.

Назад к пользовательскому интерфейсу

Чтобы использовать redux в пользовательском интерфейсе, нам нужно установить библиотеку react-redux.

npm i react-redux

По завершении установки замените код по умолчанию на этот в файле _app.js.

import "../styles/globals.css";
import store from "../store/config";
import { Provider } from "react-redux";
function MyApp({ Component, pageProps }) {
  return (
    <Provider store={store}>
      <Component {...pageProps} />
    </Provider>
  );
}
export default MyApp;

Теперь, когда мы настроили избыточность в нашем пользовательском интерфейсе, давайте перейдем к файлу index.tsx и заменим его этим обновленным кодом:

import { useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { addList, listSelector, removeList } from "../store/ducks/list";
export default function Home() {
  const [input, setInput] = useState<string>("");
  const { list } = useSelector(listSelector);
  const dispatch = useDispatch();
  const addItem = () => {
    dispatch(addList(input));
  };
  const removeItem = (value: string) => {
    dispatch(removeList(value));
  };
  return (
    <div className="conatiner">
      <div className="list-container">
        <div className="list-header">
          <h1 className="title">
            Lists<span>.</span>
          </h1>
          <div className="input-field">
            <input
              onChange={(e) => setInput(e.target.value)}
              type="text"
              className="search"
              placeholder="Add"
            />
            <button onClick={addItem} className="btn">
              Add
            </button>
          </div>
        </div>
        <div className="list-body">
          {list &&
            list.map((l: string, index: number) => (
              <div key={index} className="list-item">
                <div className="list-item-content">{l}</div>
                <button
                  onClick={() => removeItem(l)}
                  className="list-item-footer"
                >
                  X
                </button>
              </div>
            ))}
        </div>
      </div>
    </div>
  );
}

Вывод

Примечание: это лишь второстепенная основа redux-toolkit, но они гораздо больше об этой библиотеке.
Спасибо, что прочитали мой первый туториал на Medium, надеюсь, вы чему-то научились из него :).