Минуту назад кто-то задал мне вопрос о том, как он может использовать 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, надеюсь, вы чему-то научились из него :).