Понимание React🚀-Redux и использование Redux Toolkit🚀


Предварительные требования : React, Javascript ES6

🍁 Что такое Redux?

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

Давайте разберемся, как работает Redux,

Верхнее изображение представляет React без Redux. Каждый круг представляет компонент. Когда компонент инициирует изменение (синий круг), это изменение передается другим компонентам по одному шагу за раз. Это может показаться достаточно простым, когда у нас всего 10 компонентов, но как насчет приложения с 20, 50 или 100 компонентами? Когда приложение становится больше, отладка может быстро усложниться, и мы теряем из виду, как информация передается от одного компонента к другому.

Ниже показано то же приложение React с Redux. Здесь, когда компонент инициирует изменение, эта информация попадает прямо из синего круга в наш магазин (зеленый круг). Оттуда изменение передается непосредственно всем компонентам, которые нуждаются в обновлении.

🍁 Понимание управления состояниями

Управление состояниями — это способ представить данные, которые вы используете в своем приложении, в виде структуры данных. Таким образом, будет проще обмениваться этими данными между компонентами. Это облегчает коммуникацию между различными компонентами и позволяет им легко узнавать о любых обновленных данных.

В React существует множество различных библиотек для управления состоянием: Redux, Recoil, context api и т.д.

На рисунке выше показан процесс, который происходит во время управления состоянием. Синяя рамка представляет собой шаблон, и не каждый разработчик стремится использовать Redux. Но не волнуйтесь, я приведу простую аналогию, чтобы вы поняли все правильно.

🍀View

Это не что иное, как фронтенд приложения, и все изменения, которые происходят, должны отражаться на приложении. Для аналогии, представьте, что перед вами ювелирный магазин. Вы стоите перед ним и смотрите на него, если кто-то покупает или если в магазине появляются украшения, вы видите изменения, происходящие в магазине. Это вид.

🍀Store

Store в основном хранит все ваши данные управления состоянием в одном месте, поэтому, чтобы понять аналогию, магазин, о котором мы говорили в View, хранит все ювелирные изделия в своем магазине. В другом магазине хранятся другие вещи. Отсюда и магазины.

🍀Action

В терминах Redux, это действие должно быть отправлено на Reducer, чтобы управление состоянием могло быть выполнено в соответствии с отправленным действием, которое в конечном итоге будет отражено в представлении нашего приложения. Теперь мы увидели две новые вещи, которые нам необходимо понять, а именно: Действие и Отправка. Итак, чтобы понять их оба и как они связаны друг с другом. Представьте, что действие — это мысль, которая приходит вам в голову, когда вы входите в магазин, например, я хочу купить ювелирное изделие. А Диспетчер — это то, что выходит из вашего рта, по сути, ваш голос/команда/просьба, которая посылает сообщение продавцу о том, зачем вы пришли в магазин.

🍀Reducer

На языке Redux это то, что вносит изменения в объект State в Store, чтобы отразить их в View. Сложно, да? Теперь давайте разберем это на аналогии, сейчас вы зашли в магазин, и попросили (Dispatched) купить ювелирное изделие (Action). Запрос вы передали человеку, который стоит на другой стороне магазина, мы называем его/ее продавцом, но он/она также является редуктором. Ваш следующий вопрос — как? Тогда позвольте мне рассказать вам, как только вы обращаетесь с просьбой, продавец подбирает для вас нужный товар и отдает его вам в обмен на деньги. Теперь давайте на время отвлечемся от денежной части и сосредоточимся на уменьшении количества драгоценностей. Что же происходит между этими процессами? Владелец магазина (Уменьшитель), произвел изменения в магазине, уменьшив количество украшений в магазине, и, следовательно, Вид также изменился, то есть, количество украшений в магазине теперь изменилось.

🍁Redux-toolkit

Пакет Redux Toolkit предназначен для того, чтобы стать стандартным способом написания логики Redux. Для простых вещей рекомендуется использовать Redux Toolkit. Он был разработан для того, чтобы решить три распространенные проблемы Redux:

  • Настройка магазина Redux слишком сложна
  • Мне приходится добавлять множество пакетов, чтобы заставить Redux делать что-то полезное
  • Для Redux требуется слишком много шаблонного кода

Давайте используем Redux Toolkit для создания приложения To-Do.

🍀 Установка Redux Toolkit

Теперь хорошо настроим Redux Toolkit и React-Redux. Начните с запуска:

# NPM
npm install @reduxjs/toolkit
npm install react-redux

# Yarn
yarn add @reduxjs/toolkit
yarn add react-redux

Войдите в полноэкранный режим Выйдите из полноэкранного режима

🍀 Настройка магазина

Создайте файл store.js. Импортируйте API configureStore из Redux Toolkit. Мы начнем с создания пустого магазина Redux и его экспорта.

import { configureStore } from '@reduxjs/toolkit'

export const store = configureStore({
  reducer: {},
})

Вход в полноэкранный режим Выход из полноэкранного режима

🍀Конфигурирование Redux Store в React

После создания магазина мы можем сделать его доступным для наших компонентов React, поместив React-Redux вокруг нашего приложения в src/store.js. Импортируйте только что созданный магазин Redux, поместите Provider вокруг вашего приложения и передайте магазин в качестве реквизита.

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';

import store from './store';
import { Provider } from 'react-redux'

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
    <Provider store={store}>
        <App />
    </Provider>
);

Войдите в полноэкранный режим Выход из полноэкранного режима

🍀Создайте Redux Slicer

Создайте файл с именем Reducers/todoSlider.js и импортируйте createSlice из Redux Toolkit Api.

Slicer требует строковое имя для идентификации среза, начальное значение состояния и одну или несколько функций reducer для определения того, как состояние может быть обновлено. Slicer позволяет нам определить начальное состояние, создать действие и редуктор в одном месте.

import { createSlice } from '@reduxjs/toolkit'
export const toDoSlider = createSlice({
 name: 'toDo',
 initialState: {
   todoList: [
     { id: 1, content: "Hit the gym" },
     { id: 2, content: "Meet George"}
   ]
 },
 reducers: {
   addToDo: (state, action) => {
     let newTodoList = {
       id: Math.random(),
       content: action.payload.newContent
     }
     state.todoList.push(newTodoList);
   },
   deleteToDo: (state, action) => {
     let { todoList } = state;
     state.todoList = todoList.filter((item) => 
         item.id !==action.payload.id);
   },
   editTodo: (state, action) => {
     let { todoList } = state;
     state.todoList = todoList.map((item) => 
       item.id === action.payload.id ? action.payload : item);
   }
  },
})
// Action creators are generated for each case reducer function
export const { addToDo, deleteToDo, editTodo } = toDoSlider.actions
export default toDoSlider.reducer;

Вход в полноэкранный режим Выход из полноэкранного режима

Из приведенного выше кода видно, что начальное состояние определено, а функции reducers созданы для выполнения операций добавления, обновления и удаления.

🍀Добавление редукторов срезов в хранилище

Далее нам нужно импортировать функцию reducer из фрагмента счетчика и добавить ее в наш магазин внутри src/store.js . Определяя поле внутри параметра reducer, мы указываем магазину использовать эту функцию редуктора среза для обработки всех обновлений этого состояния.

import { configureStore } from '@reduxjs/toolkit'

export const store = configureStore({
  reducer: {
        toDo: toDoReducer;
    },
})

Вход в полноэкранный режим Выход из полноэкранного режима

🍀Создание компонентов и импорт в app.js

Создайте файл с именем src/Components/AddTodo.js.

import React, { useState } from 'react';
import { useDispatch } from 'react-redux';
import { addToDo } from '../Reducers/toDoSlider';
const AddTodo = () => {
  const dispatch = useDispatch();
  const [state, setState] = useState({
     content: '',
     contentError: null
  });
  const handleChange = (e) =>{
    setState({...state, 
          [e.target.name]: e.target.value,
          [`${e.target.name}Error`]: null });
  }
  const add = () =>{
    if(content === ''){
      setState({...state, 
         contentError: 'You must write something!'});
       return;
    }
    dispatch(addToDo({newContent: content}));
    setState({...state, content: ''});
  }
  const { content, contentError } = state;
   return <div className='form'>
      <h2>What's your plan for today</h2>
      <input type='text' value={content} 
        name='content' 
        onChange={handleChange}>
      </input>
      <button type='button' className='button' 
        onClick={add}>Add
      </button>
      {contentError ? 
         <div className='error'>{contentError}</div>: null}
    </div>;
};
export default AddTodo;

Войдите в полноэкранный режим Выйдите из полноэкранного режима

Из приведенного выше кода видно, что хук Redux useDispatch используется для диспетчеризации действия addToDo для обновления состояния.

Создайте файл с именем src/Components/ListTodo.js.

import React, { useState } from 'react';
import { AiFillEdit, AiOutlineCloseCircle } from "react-icons/ai";
import { useDispatch, useSelector } from 'react-redux';
import { deleteToDo, editTodo } from '../Reducers/toDoSlider';
const ListTodo = () => {
  const { todoList } = useSelector((state) => state.toDo);
  const dispatch = useDispatch();
  const [isEditing, setEditing] = useState(false);
  const [state, setState] = useState({
   id: '', content: '', contentError: null
  });
  const onEditToggle = ( id, content) => {
   setEditing(true);
   setState({ ...state, id, content});
  }
  const handleChange = (e) =>{
   setState({...state, [e.target.name]: e.target.value,  
      [`${e.target.name}Error`]: null });
  }
  const { content, contentError, id } = state;
  const edit = () =>{
   if(content === ''){
    setState({...state, contentError: 'You must write something!'});
    return;
   }
   dispatch((editTodo({content, id})));
   setEditing(false);
  }
return <div>
 {
   isEditing ?
    <div className='form'>
      <h2>Update your plan for today</h2>
      <input type='text' value={content} name='content' 
         onChange={handleChange}>
      </input>
      <button type='button' className='button' 
        onClick={edit}>Edit
     </button>
     {contentError ? 
       <div className='error'>{contentError}</div>: null
     }
   </div> :
   <ul className='todos'>
    {
      todoList.map(({id, content})=> {
        return <li className='grid' key={id}>
          <span className='content'>{content}</span>
          <span className='todo-action'>  
            <AiOutlineCloseCircle className="close" 
              onClick={() => dispatch(deleteToDo({id}))}
            />
            <AiFillEdit className="edit" 
              onClick={() =>onEditToggle(id, content)} 
            />
          </span>
       </li>
     })
    }
  </ul>
  }
</div>;
};
export default ListTodo;

Войдите в полноэкранный режим Выйдите из полноэкранного режима

В приведенном выше компоненте Redux хук useDispatch используется для диспетчеризации действий deleteToDo, editTodo и useSelector для получения состояния из reducer.

Импорт компонентов в App.js

import './App.css';
import AddTodo from './Components/AddTodo';
import ListTodo from './Components/ListTodo';
function App() {
  return (
    <div className="App">
      <AddTodo />
      <ListTodo />
    </div>
  );
}
export default App;

Вход в полноэкранный режим Выход из полноэкранного режима

🍀Finally, добавьте стили в app.css

/* variables */
:root{
  --primary: #FFC636;
  --secondary: #0A0B5B;
}
/* reset */
body,p,a,ul,li{
 margin: 0;
 padding: 0;
 text-decoration: none;
}
li{
 list-style-type: none;
}
/* base styles */
body{
 background: var(--secondary);
 overflow-x: hidden;
}
.button{
 background: none;
 border: 2px solid var(--primary);
 color: var(--primary);
 padding: 6px 12px;
 border-radius: 10px;
 text-transform: uppercase;
 box-shadow: 1px 2px 3px rgba(0,0,0,0.6);
 display: inline-block;
 font-size: 1em;
 margin-left: 5px;
}
.button:hover{
 color: #222;
 background: var(--primary);
}
input{
 background: rgba(255,255,255,0.05);
 padding: 10px 16px;
 border-radius: 10px;
 border: 2px solid #9893D8;
 color: #f2f2f2;
 font-size: 1em;
}
.error{
 color: rgb(187, 30, 30);
 text-align: left;
 margin-left: 2px;
}
/* fonts */
body{
 color: #f2f2f2;
}
/* mobile styles */
.App{
 margin: 5%;
}
.form{
 text-align: center;
}
.grid{
 display: grid;
 grid-template-columns: repeat(8, 1fr);
 gap: 10px;
 box-sizing: border-box;
}
.todos{
 margin-top: 20px;
}
.todos li{
 margin-top: 10px;
 background-color: #6767ab;
 padding: 3%;
 box-shadow: 1px 2px 3px honeydew;
 cursor: pointer;
}
.content{
 grid-column: 1/8;
}
.todo-action{
 display: flex;
 font-size: 1.5em
}
/* small tablet styles */
@media screen and (min-width: 620px){
 input{
  width: 300px;
 }
 .todos li{
   margin: 10px 20%;
   padding: 2%;
  }
 }
/* large tablet & laptop styles */
@media screen and (min-width: 960px){
.todos li{
  margin: 10px 25%;
 }
}
@media screen and (min-width: 1200px){
 .todos li{
  margin: 10px 33%;
  padding: 1%;
 }
}

Войти в полноэкранный режим Выйти из полноэкранного режима

🍀Вывод

Надеюсь, вам понравился этот небольшой урок. Счастливого кодинга💗

Всегда помните, что никто не достиг вершины одним выстрелом. Им потребовалось гораздо больше борьбы и тяжелой работы, чем вы можете себе представить. Так что стремитесь к знаниям и продолжайте двигаться вперед. Спасибо

Оцените статью
devanswers.ru
Добавить комментарий