From 3098a354c462f8b2f9b72ff85b8e5f4456b3de60 Mon Sep 17 00:00:00 2001 From: Misha Grinko Date: Sun, 9 Jun 2024 17:14:24 +0300 Subject: [PATCH] use @redux/toolkit --- README.md | 2 ++ package-lock.json | 44 ++++++++++++++++++++++++++++--------- package.json | 6 ++--- src/App.tsx | 42 ++++++++++++++--------------------- src/app/hooks.ts | 5 ----- src/app/store.ts | 22 +++++-------------- src/components/index.ts | 4 ++++ src/features/currentTodo.ts | 37 +++++-------------------------- src/features/filter.ts | 18 +++++++-------- src/features/todos.ts | 13 +++++------ src/index.tsx | 11 ++-------- 11 files changed, 87 insertions(+), 117 deletions(-) delete mode 100644 src/app/hooks.ts create mode 100644 src/components/index.ts diff --git a/README.md b/README.md index 59e7b6094..8aca9f189 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # React + Redux list of TODOs +> ❗ Read the lesson theory before solving this task + You are given an `app` folder with already implemented `store` and `hooks`. Use them to implement [Dynamic list of TODOs](https://github.com/mate-academy/react_dynamic-list-of-todos#react-dynamic-list-of-todos) using the Redux. It should look and work identically, so use the same markup. diff --git a/package-lock.json b/package-lock.json index 0bb732fb8..56f5309a8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2777,6 +2777,34 @@ "source-map": "^0.7.3" } }, + "@reduxjs/toolkit": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.2.5.tgz", + "integrity": "sha512-aeFA/s5NCG7NoJe/MhmwREJxRkDs0ZaSqt0MxhWUrwCf1UQXpwR87RROJEql0uAkLI6U7snBOYOcKw83ew3FPg==", + "requires": { + "immer": "^10.0.3", + "redux": "^5.0.1", + "redux-thunk": "^3.1.0", + "reselect": "^5.1.0" + }, + "dependencies": { + "immer": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz", + "integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==" + }, + "redux": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", + "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==" + }, + "redux-thunk": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz", + "integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==" + } + } + }, "@remix-run/router": { "version": "1.15.3", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.3.tgz", @@ -13387,20 +13415,11 @@ "version": "4.2.1", "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", + "dev": true, "requires": { "@babel/runtime": "^7.9.2" } }, - "redux-devtools-extension": { - "version": "2.13.9", - "resolved": "https://registry.npmjs.org/redux-devtools-extension/-/redux-devtools-extension-2.13.9.tgz", - "integrity": "sha512-cNJ8Q/EtjhQaZ71c8I9+BPySIBVEKssbPpskBfsXqb8HJ002A3KRVHfeRzwRo6mGPqsm7XuHTqNSNeS1Khig0A==" - }, - "redux-thunk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz", - "integrity": "sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==" - }, "reflect.getprototypeof": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz", @@ -13645,6 +13664,11 @@ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" }, + "reselect": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", + "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==" + }, "resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", diff --git a/package.json b/package.json index 84c03f2bb..a982e687d 100644 --- a/package.json +++ b/package.json @@ -8,16 +8,14 @@ "dependencies": { "@cypress/react18": "^2.0.0", "@fortawesome/fontawesome-free": "^6.2.0", + "@reduxjs/toolkit": "^2.2.5", "bulma": "^0.9.4", "classnames": "^2.5.1", "react": "^18.2.0", "react-dom": "^18.2.0", "react-redux": "^8.1.3", "react-router-dom": "^6.3.0", - "react-scripts": "^5.0.1", - "redux": "^4.2.1", - "redux-devtools-extension": "^2.13.9", - "redux-thunk": "^2.4.2" + "react-scripts": "^5.0.1" }, "devDependencies": { "@babel/plugin-proposal-private-property-in-object": "^7.21.11", diff --git a/src/App.tsx b/src/App.tsx index d46111e82..e10753461 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,34 +1,26 @@ -/* eslint-disable max-len */ -import React from 'react'; import 'bulma/css/bulma.css'; import '@fortawesome/fontawesome-free/css/all.css'; +import { Loader, TodoFilter, TodoList, TodoModal } from './components'; -import { TodoList } from './components/TodoList'; -import { TodoFilter } from './components/TodoFilter'; -import { TodoModal } from './components/TodoModal'; -import { Loader } from './components/Loader'; +export const App = () => ( + <> +
+
+
+

Todos:

-export const App: React.FC = () => { - return ( - <> -
-
-
-

Todos:

- -
- -
+
+ +
-
- - -
+
+ +
+
- - - ); -}; + + +); diff --git a/src/app/hooks.ts b/src/app/hooks.ts deleted file mode 100644 index 6ed6fde70..000000000 --- a/src/app/hooks.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'; -import { AppDispatch, RootState } from './store'; - -export const useAppDispatch: () => AppDispatch = useDispatch; -export const useAppSelector: TypedUseSelectorHook = useSelector; diff --git a/src/app/store.ts b/src/app/store.ts index 821420e8a..3a9b384be 100644 --- a/src/app/store.ts +++ b/src/app/store.ts @@ -1,22 +1,10 @@ -import { createStore, applyMiddleware, combineReducers } from 'redux'; -import { composeWithDevTools } from 'redux-devtools-extension'; -import thunk from 'redux-thunk'; +import { combineSlices, configureStore } from '@reduxjs/toolkit'; -import currentTodoReducer from '../features/currentTodo'; -import filterReducer from '../features/filter'; -import todosReducer from '../features/todos'; +const rootReducer = combineSlices(); -const rootReducer = combineReducers({ - currentTodo: currentTodoReducer, - filter: filterReducer, - todos: todosReducer, +export const store = configureStore({ + reducer: rootReducer, }); -// The `store` is passed to the Provider in `/src/index.tsx` -export const store = createStore( - rootReducer, - composeWithDevTools(applyMiddleware(thunk)), -); - -export type RootState = ReturnType; +export type RootState = ReturnType; export type AppDispatch = typeof store.dispatch; diff --git a/src/components/index.ts b/src/components/index.ts new file mode 100644 index 000000000..bce637539 --- /dev/null +++ b/src/components/index.ts @@ -0,0 +1,4 @@ +export * from './Loader'; +export * from './TodoList'; +export * from './TodoFilter'; +export * from './TodoModal'; diff --git a/src/features/currentTodo.ts b/src/features/currentTodo.ts index d8eefa90f..9e69e2240 100644 --- a/src/features/currentTodo.ts +++ b/src/features/currentTodo.ts @@ -1,35 +1,10 @@ +import { createSlice } from '@reduxjs/toolkit'; import { Todo } from '../types/Todo'; -// we use string literal as a type to avoid mistype in future -type RemoveTodoAction = { type: 'currentTodo/REMOVE' }; +const initialState = null as Todo | null; -// payload is a typical name for an action data -type SetTodoAction = { - type: 'currentTodo/SET'; - payload: Todo; -}; - -// Action creator return type protect us from a mistype -const removeTodo = (): RemoveTodoAction => ({ type: 'currentTodo/REMOVE' }); - -const setTodo = (todo: Todo): SetTodoAction => ({ - type: 'currentTodo/SET', - payload: todo, +export const currentTodoSlice = createSlice({ + name: 'currentTodo', + initialState, + reducers: {}, }); - -// These actions will be used in the application -export const actions = { setTodo, removeTodo }; - -type State = Todo | null; -type Action = SetTodoAction | RemoveTodoAction; - -const currentTodoReducer = (state: State = null, action: Action): State => { - switch (action.type) { - // Implement all actions here - - default: - return state; - } -}; - -export default currentTodoReducer; diff --git a/src/features/filter.ts b/src/features/filter.ts index 16ce5948e..d0eaf4640 100644 --- a/src/features/filter.ts +++ b/src/features/filter.ts @@ -1,12 +1,12 @@ -export const actions = { - /* put action creators here */ -}; +import { createSlice } from '@reduxjs/toolkit'; -const filterReducer = () => { - return { - query: '', - status: 'all', - }; +const initialState = { + query: '', + status: 'all', }; -export default filterReducer; +export const filterSlice = createSlice({ + name: 'filter', + initialState, + reducers: {}, +}); diff --git a/src/features/todos.ts b/src/features/todos.ts index c4656c73a..4555dea8d 100644 --- a/src/features/todos.ts +++ b/src/features/todos.ts @@ -1,9 +1,8 @@ +import { createSlice } from '@reduxjs/toolkit'; import { Todo } from '../types/Todo'; -export const actions = {}; - -const todosReducer = (): Todo[] => { - return []; -}; - -export default todosReducer; +export const todosSlice = createSlice({ + name: 'todos', + initialState: [] as Todo[], + reducers: {}, +}); diff --git a/src/index.tsx b/src/index.tsx index dea7d1a43..441b66c72 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -5,17 +5,10 @@ import { HashRouter as Router } from 'react-router-dom'; import { store } from './app/store'; import { App } from './App'; -const container = document.getElementById('root') as HTMLElement; -const root = createRoot(container); - -// Just a convenient component with all the wrappers for the `App` -// The Router component (if you use it) should be placed inside the Provider -const Root = () => ( +createRoot(document.getElementById('root') as HTMLElement).render( - + , ); - -root.render();