К концу этого блога вы будете знать, как настроить Redux Toolkit и создать приложение To do. Для этого урока я предполагаю, что вы уже знакомы с React.

Почему мне следует использовать инструментарий Redux?

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

Шаги:

  1. Выполните указанную ниже команду, удалите ненужные коды и файлы, такие как логотип React, и протестируйте свое приложение.
npx create-react-app React-Redux-Toolkit-TODOAPP
cd React-Redux-Toolkit-TODOAPP
npm start

2. Установите необходимые пакеты.

npm install @reduxjs/toolkit react-redux react-icons

@reduxjs/toolkit react-redux: библиотека инструментов Redux и Redux.

react-icons: легко включайте популярные значки в свои проекты React с помощью react-icons, который использует импорт ES6, что позволяет вам включать только значки, которые использует ваш проект.

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

import { configureStore } from '@reduxjs/toolkit'
export default configureStore({
  reducer: {}
})

4. Предоставьте магазин Redux для реагирования.

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

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import store from './store';
import { Provider } from 'react-redux';
ReactDOM.render(
  <React.StrictMode>
     <Provider store={store}>
       <App />
     </Provider>
   </React.StrictMode>,
document.getElementById('root')
);
reportWebVitals();

5. Создайте слайдер Redux

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

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

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;

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

6. Добавьте редукторы Slice в магазин

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

import { configureStore } from '@reduxjs/toolkit'
import toDoReducer from './Reducers/toDoSlider';
export default configureStore({
 reducer: {// allows you create n number of sliders
     toDo: toDoReducer
  ,
})

7. Создание компонентов и импорт в приложение .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 hook useDispatch используется для отправки действий deleteToDo, editTodo и useSelector для получения состояния от редуктора.

Импорт компонентов в 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;

7.Добавьте стили в 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%;
 }
}

Вывод:

Git-хаб