diff --git a/package.json b/package.json index a1dbacd..4a574f0 100644 --- a/package.json +++ b/package.json @@ -7,12 +7,14 @@ "@testing-library/jest-dom": "^5.14.1", "@testing-library/react": "^13.0.0", "@testing-library/user-event": "^13.2.1", + "jotai": "2.2.2", "react": "^18.2.0", "react-dom": "^18.2.0", "react-redux": "8.1.1", "react-scripts": "5.0.1", "styled-components": "6.0.5", - "web-vitals": "^2.1.0" + "web-vitals": "^2.1.0", + "zustand": "4.3.9" }, "scripts": { "start": "react-scripts start", diff --git a/src/App.js b/src/App.js index 1eade34..94d80f4 100644 --- a/src/App.js +++ b/src/App.js @@ -6,7 +6,7 @@ import TodoList from "./components/TodoList"; const App = () => { return ( - 리덕스로 만드는 투두리스트 + jotai로 만드는 투두리스트 diff --git a/src/components/TodoInput.jsx b/src/components/TodoInput.jsx index 8378dc3..f616ab0 100644 --- a/src/components/TodoInput.jsx +++ b/src/components/TodoInput.jsx @@ -1,23 +1,18 @@ import React, { useState } from "react"; import { styled } from "styled-components"; -import { useDispatch } from "react-redux"; -import { addTodo } from "../redux/modules/todoSlice"; +import { todosAtom } from "../store/atom"; +import { useAtom } from "jotai"; const TodoInput = () => { const [title, setTitle] = useState(""); const [content, setContent] = useState(""); - const dispatch = useDispatch(); + const [_, setTodos] = useAtom(todosAtom); const handleClick = () => { - dispatch( - addTodo({ - id: Date.now(), - title, - content, - isDone: false, - }) - ); + setTodos((prev) => { + return [...prev, { id: Date.now(), title, content, isDone: false }]; + }); }; return ( diff --git a/src/components/TodoItem.jsx b/src/components/TodoItem.jsx index 039b270..da1a321 100644 --- a/src/components/TodoItem.jsx +++ b/src/components/TodoItem.jsx @@ -1,20 +1,29 @@ import React from "react"; import { styled } from "styled-components"; -import { useDispatch } from "react-redux"; -import { deleteTodo, updateTodo } from "../redux/modules/todoSlice"; +import { todosAtom } from "../store/atom"; +import { useAtom } from "jotai"; const TodoItem = (props) => { const { todo } = props; const { id, title, content, isDone } = todo; - - const dispatch = useDispatch(); + const [_, setTodos] = useAtom(todosAtom); const deleteTodoItem = (id) => { - dispatch(deleteTodo(id)); + setTodos((prev) => { + return prev.filter((todo) => todo.id !== id); + }); }; const updateTodoItem = (id) => { - dispatch(updateTodo(id)); + setTodos((prev) => { + return prev.map((todo) => { + if (todo.id === id) { + return { ...todo, isDone: !todo.isDone }; + } else { + return todo; + } + }); + }); }; return ( diff --git a/src/components/TodoList.jsx b/src/components/TodoList.jsx index a57765a..beb3d71 100644 --- a/src/components/TodoList.jsx +++ b/src/components/TodoList.jsx @@ -1,10 +1,11 @@ import React from "react"; import { styled } from "styled-components"; import TodoItem from "./TodoItem"; -import { useSelector } from "react-redux"; +import { useAtom } from "jotai"; +import { todosAtom } from "../store/atom"; const TodoList = () => { - const { todos } = useSelector((state) => state.todoReducer); + const [todos] = useAtom(todosAtom); return ( diff --git a/src/index.js b/src/index.js index a2d4630..fc0b2a1 100644 --- a/src/index.js +++ b/src/index.js @@ -1,15 +1,11 @@ import React from "react"; import ReactDOM from "react-dom/client"; import App from "./App"; -import { Provider } from "react-redux"; -import { store } from "./redux/config/configStore"; const root = ReactDOM.createRoot(document.getElementById("root")); root.render( - - - - - + + + ); diff --git a/src/redux/config/configStore.js b/src/redux/config/configStore.js deleted file mode 100644 index 42609cf..0000000 --- a/src/redux/config/configStore.js +++ /dev/null @@ -1,9 +0,0 @@ -import { configureStore } from "@reduxjs/toolkit"; - -import todoReducer from "../modules/todoSlice"; - -export const store = configureStore({ - reducer: { - todoReducer, - }, -}); diff --git a/src/redux/modules/todoSlice.js b/src/redux/modules/todoSlice.js deleted file mode 100644 index 3ee6d00..0000000 --- a/src/redux/modules/todoSlice.js +++ /dev/null @@ -1,30 +0,0 @@ -import { createSlice } from "@reduxjs/toolkit"; - -const name = "todos"; - -const initialState = { - todos: [], -}; - -export const counterSlice = createSlice({ - name, - initialState, - reducers: { - addTodo: (state, action) => { - state.todos.push(action.payload); - }, - updateTodo: (state, action) => { - const todo = state.todos.find((todo) => todo.id === action.payload); - todo.isDone = !todo.isDone; - }, - deleteTodo: (state, action) => { - state.todos = state.todos.filter((todo) => todo.id !== action.payload); - }, - }, - extraReducers: {}, -}); - -// Action creators are generated for each case reducer function -export const { addTodo, deleteTodo, updateTodo } = counterSlice.actions; - -export default counterSlice.reducer; diff --git a/src/store/atom.js b/src/store/atom.js new file mode 100644 index 0000000..f060f9e --- /dev/null +++ b/src/store/atom.js @@ -0,0 +1,3 @@ +import { atom } from "jotai"; + +export const todosAtom = atom([]); diff --git a/yarn.lock b/yarn.lock index 65ff377..8a86bae 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6110,6 +6110,11 @@ jiti@^1.18.2: resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.19.1.tgz#fa99e4b76a23053e0e7cde098efe1704a14c16f1" integrity sha512-oVhqoRDaBXf7sjkll95LHVS6Myyyb1zaunVwk4Z0+WPSW4gjS0pl01zYKHScTuyEhQsFxV5L4DR5r+YqSyqyyg== +jotai@2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/jotai/-/jotai-2.2.2.tgz#1e181789dcc01ced8240b18b95d9fa10169f9366" + integrity sha512-Cn8hnBg1sc5ppFwEgVGTfMR5WSM0hbAasd/bdAwIwTaum0j3OUPqBSC4tyk3jtB95vicML+RRWgKFOn6gtfN0A== + "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -9083,7 +9088,7 @@ url-parse@^1.5.3: querystringify "^2.1.1" requires-port "^1.0.0" -use-sync-external-store@^1.0.0: +use-sync-external-store@1.2.0, use-sync-external-store@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== @@ -9649,3 +9654,10 @@ yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +zustand@4.3.9: + version "4.3.9" + resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.3.9.tgz#a7d4332bbd75dfd25c6848180b3df1407217f2ad" + integrity sha512-Tat5r8jOMG1Vcsj8uldMyqYKC5IZvQif8zetmLHs9WoZlntTHmIoNM8TpLRY31ExncuUvUOXehd0kvahkuHjDw== + dependencies: + use-sync-external-store "1.2.0"