├── src ├── App.css ├── app │ └── store.js ├── setupTests.js ├── App.test.js ├── App.js ├── index.css ├── reportWebVitals.js ├── components │ ├── Todos.js │ ├── AddTodo.js │ └── Todo.js ├── index.js └── features │ └── todos │ └── todosSlice.js ├── public ├── robots.txt ├── favicon.ico ├── logo192.png ├── logo512.png ├── app-screenshoot.png ├── manifest.json └── index.html ├── README.md ├── .gitignore └── package.json /src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | margin: 50px 3 | } 4 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raaynaldo/redux-toolkit-tutorial/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raaynaldo/redux-toolkit-tutorial/HEAD/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raaynaldo/redux-toolkit-tutorial/HEAD/public/logo512.png -------------------------------------------------------------------------------- /public/app-screenshoot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raaynaldo/redux-toolkit-tutorial/HEAD/public/app-screenshoot.png -------------------------------------------------------------------------------- /src/app/store.js: -------------------------------------------------------------------------------- 1 | import { configureStore } from '@reduxjs/toolkit'; 2 | import todosReducer from '../features/todos/todosSlice'; 3 | 4 | export default configureStore({ 5 | reducer: { 6 | todos: todosReducer, 7 | }, 8 | }); 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Redux Toolkit Tutorial 2 | 3 | ![app screenshoot](https://raw.githubusercontent.com/raaynaldo/redux-toolkit-tutorial/main/public/app-screenshoot.png) 4 | 5 | Read the full tutorial blog [here](https://dev.to/raaynaldo/redux-toolkit-setup-tutorial-5fjf) 6 | -------------------------------------------------------------------------------- /src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import Todos from './components/Todos'; 2 | import AddTodo from './components/AddTodo'; 3 | 4 | import './App.css'; 5 | 6 | function App() { 7 | return ( 8 |
9 | 10 | 11 |
12 | ); 13 | } 14 | 15 | export default App; 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /src/components/Todos.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useSelector } from 'react-redux'; 3 | import Todo from './Todo'; 4 | 5 | export default function Todos() { 6 | const todos = useSelector((state) => state.todos); 7 | 8 | return ( 9 |
10 | 17 |
18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /src/components/AddTodo.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { useDispatch } from 'react-redux'; 3 | import { addTodo } from '../features/todos/todosSlice'; 4 | 5 | export default function AddTodo() { 6 | const [text, setText] = useState(''); 7 | const dispatch = useDispatch(); 8 | 9 | const addTodoHandler = (event) => { 10 | event.preventDefault(); 11 | dispatch(addTodo(text)); 12 | setText(''); 13 | }; 14 | 15 | return ( 16 |
17 | setText(e.target.value)} 21 | /> 22 | 23 |
24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import './index.css'; 4 | import App from './App'; 5 | import reportWebVitals from './reportWebVitals'; 6 | import { Provider } from 'react-redux'; 7 | import store from './app/store'; 8 | 9 | const root = ReactDOM.createRoot(document.getElementById('root')); 10 | root.render( 11 | 12 | 13 | 14 | 15 | 16 | ); 17 | 18 | // If you want to start measuring performance in your app, pass a function 19 | // to log results (for example: reportWebVitals(console.log)) 20 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 21 | reportWebVitals(); 22 | -------------------------------------------------------------------------------- /src/features/todos/todosSlice.js: -------------------------------------------------------------------------------- 1 | import { createSlice } from '@reduxjs/toolkit'; 2 | import { v4 as uuid } from 'uuid'; 3 | 4 | export const todosSlice = createSlice({ 5 | name: 'todos', 6 | initialState: [], 7 | reducers: { 8 | addTodo: (state, action) => { 9 | const todo = { 10 | id: uuid(), 11 | text: action.payload, 12 | }; 13 | 14 | 15 | return [...state, todo]; 16 | }, 17 | updateTodo: (state, action) => { 18 | const { id, text } = action.payload; 19 | 20 | const todo = state.find((todo) => todo.id === id); 21 | todo.text = text; 22 | }, 23 | deleteTodo: (state, action) => { 24 | return state.filter((todo) => todo.id !== action.payload); 25 | }, 26 | }, 27 | }); 28 | 29 | export const { addTodo, updateTodo, deleteTodo } = todosSlice.actions; 30 | 31 | export default todosSlice.reducer; 32 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "redux-toolkit-tutorial", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@reduxjs/toolkit": "^1.8.1", 7 | "@testing-library/jest-dom": "^5.16.4", 8 | "@testing-library/react": "^13.1.1", 9 | "@testing-library/user-event": "^13.5.0", 10 | "react": "^18.0.0", 11 | "react-dom": "^18.0.0", 12 | "react-redux": "^8.0.0", 13 | "react-scripts": "5.0.1", 14 | "uuid": "^8.3.2", 15 | "web-vitals": "^2.1.4" 16 | }, 17 | "scripts": { 18 | "start": "react-scripts start", 19 | "build": "react-scripts build", 20 | "test": "react-scripts test", 21 | "eject": "react-scripts eject" 22 | }, 23 | "eslintConfig": { 24 | "extends": [ 25 | "react-app", 26 | "react-app/jest" 27 | ] 28 | }, 29 | "browserslist": { 30 | "production": [ 31 | ">0.2%", 32 | "not dead", 33 | "not op_mini all" 34 | ], 35 | "development": [ 36 | "last 1 chrome version", 37 | "last 1 firefox version", 38 | "last 1 safari version" 39 | ] 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/components/Todo.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { useDispatch } from 'react-redux'; 3 | import { deleteTodo, updateTodo } from '../features/todos/todosSlice'; 4 | 5 | export default function Todo(props) { 6 | const { todo } = props; 7 | const dispatch = useDispatch(); 8 | 9 | const [isEdit, setIsEdit] = useState(false); 10 | const [editValue, setEditValue] = useState(todo.text); 11 | 12 | const saveTodo = () => { 13 | const payload = { 14 | id: todo.id, 15 | text: editValue, 16 | }; 17 | dispatch(updateTodo(payload)); 18 | cancelEdit(); 19 | }; 20 | 21 | const cancelEdit = () => { 22 | setIsEdit(false); 23 | }; 24 | 25 | return isEdit ? ( 26 |
27 | setEditValue(e.target.value)} 31 | /> 32 | 33 | 34 |
35 | ) : ( 36 | <> 37 | {todo.text} 38 | 39 | 40 | 41 | ); 42 | } 43 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | --------------------------------------------------------------------------------