From ae9d6afe2d02ce221470331dc86734e50cb2c050 Mon Sep 17 00:00:00 2001 From: donghun Date: Mon, 24 Jul 2023 23:56:34 +0900 Subject: [PATCH 1/3] Refactor as Zustand --- package.json | 3 ++- src/components/TodoInput.jsx | 19 ++++++++----------- src/components/TodoItem.jsx | 9 ++++----- src/components/TodoList.jsx | 4 ++-- src/index.js | 10 +++------- src/redux/config/configStore.js | 9 --------- src/redux/modules/todoSlice.js | 30 ------------------------------ src/store/useTodoStore.js | 17 +++++++++++++++++ yarn.lock | 9 ++++++++- 9 files changed, 44 insertions(+), 66 deletions(-) delete mode 100644 src/redux/config/configStore.js delete mode 100644 src/redux/modules/todoSlice.js create mode 100644 src/store/useTodoStore.js diff --git a/package.json b/package.json index a1dbacd..7784d15 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,8 @@ "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/components/TodoInput.jsx b/src/components/TodoInput.jsx index 8378dc3..e52146f 100644 --- a/src/components/TodoInput.jsx +++ b/src/components/TodoInput.jsx @@ -1,23 +1,20 @@ import React, { useState } from "react"; import { styled } from "styled-components"; -import { useDispatch } from "react-redux"; -import { addTodo } from "../redux/modules/todoSlice"; +import { useTodoStore } from "../store/useTodoStore"; const TodoInput = () => { const [title, setTitle] = useState(""); const [content, setContent] = useState(""); - const dispatch = useDispatch(); + const { addTodo } = useTodoStore((state) => state); const handleClick = () => { - dispatch( - addTodo({ - id: Date.now(), - title, - content, - isDone: false, - }) - ); + addTodo({ + id: Date.now(), + title, + content, + isDone: false, + }); }; return ( diff --git a/src/components/TodoItem.jsx b/src/components/TodoItem.jsx index 039b270..5cebd1a 100644 --- a/src/components/TodoItem.jsx +++ b/src/components/TodoItem.jsx @@ -1,20 +1,19 @@ import React from "react"; import { styled } from "styled-components"; -import { useDispatch } from "react-redux"; -import { deleteTodo, updateTodo } from "../redux/modules/todoSlice"; +import { useTodoStore } from "../store/useTodoStore"; const TodoItem = (props) => { const { todo } = props; const { id, title, content, isDone } = todo; - const dispatch = useDispatch(); + const { deleteTodo, updateTodo } = useTodoStore((state) => state); const deleteTodoItem = (id) => { - dispatch(deleteTodo(id)); + deleteTodo(id); }; const updateTodoItem = (id) => { - dispatch(updateTodo(id)); + updateTodo(id); }; return ( diff --git a/src/components/TodoList.jsx b/src/components/TodoList.jsx index a57765a..548a068 100644 --- a/src/components/TodoList.jsx +++ b/src/components/TodoList.jsx @@ -1,10 +1,10 @@ import React from "react"; import { styled } from "styled-components"; import TodoItem from "./TodoItem"; -import { useSelector } from "react-redux"; +import { useTodoStore } from "../store/useTodoStore"; const TodoList = () => { - const { todos } = useSelector((state) => state.todoReducer); + const todos = useTodoStore((state) => state.todos); 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/useTodoStore.js b/src/store/useTodoStore.js new file mode 100644 index 0000000..9d1c4fc --- /dev/null +++ b/src/store/useTodoStore.js @@ -0,0 +1,17 @@ +import { create } from "zustand"; + +export const useTodoStore = create((set) => ({ + todos: [], + addTodo: (newTodo) => set((state) => ({ todos: [...state.todos, newTodo] })), + updateTodo: (id) => + set((state) => ({ + todos: state.todos.map((todo) => { + if (todo.id === id) { + return { ...todo, isDone: !todo.isDone }; + } + return todo; + }), + })), + deleteTodo: (id) => + set((state) => ({ todos: state.todos.filter((todo) => todo.id !== id) })), +})); diff --git a/yarn.lock b/yarn.lock index 65ff377..ce9e57d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9083,7 +9083,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 +9649,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" From f59af6bce7a409717527a9db2890fa581d536eb1 Mon Sep 17 00:00:00 2001 From: donghun Date: Tue, 25 Jul 2023 00:05:17 +0900 Subject: [PATCH 2/3] Refactor as jotai --- package.json | 1 + src/components/TodoInput.jsx | 12 +++++------- src/components/TodoItem.jsx | 20 +++++++++++++++----- src/components/TodoList.jsx | 5 +++-- src/store/atom.js | 3 +++ src/store/useTodoStore.js | 17 ----------------- yarn.lock | 5 +++++ 7 files changed, 32 insertions(+), 31 deletions(-) create mode 100644 src/store/atom.js delete mode 100644 src/store/useTodoStore.js diff --git a/package.json b/package.json index 7784d15..4a574f0 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "@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", diff --git a/src/components/TodoInput.jsx b/src/components/TodoInput.jsx index e52146f..f616ab0 100644 --- a/src/components/TodoInput.jsx +++ b/src/components/TodoInput.jsx @@ -1,19 +1,17 @@ import React, { useState } from "react"; import { styled } from "styled-components"; -import { useTodoStore } from "../store/useTodoStore"; +import { todosAtom } from "../store/atom"; +import { useAtom } from "jotai"; const TodoInput = () => { const [title, setTitle] = useState(""); const [content, setContent] = useState(""); - const { addTodo } = useTodoStore((state) => state); + const [_, setTodos] = useAtom(todosAtom); const handleClick = () => { - addTodo({ - id: Date.now(), - title, - content, - isDone: false, + setTodos((prev) => { + return [...prev, { id: Date.now(), title, content, isDone: false }]; }); }; diff --git a/src/components/TodoItem.jsx b/src/components/TodoItem.jsx index 5cebd1a..da1a321 100644 --- a/src/components/TodoItem.jsx +++ b/src/components/TodoItem.jsx @@ -1,19 +1,29 @@ import React from "react"; import { styled } from "styled-components"; -import { useTodoStore } from "../store/useTodoStore"; +import { todosAtom } from "../store/atom"; +import { useAtom } from "jotai"; const TodoItem = (props) => { const { todo } = props; const { id, title, content, isDone } = todo; - - const { deleteTodo, updateTodo } = useTodoStore((state) => state); + const [_, setTodos] = useAtom(todosAtom); const deleteTodoItem = (id) => { - deleteTodo(id); + setTodos((prev) => { + return prev.filter((todo) => todo.id !== id); + }); }; const updateTodoItem = (id) => { - 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 548a068..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 { useTodoStore } from "../store/useTodoStore"; +import { useAtom } from "jotai"; +import { todosAtom } from "../store/atom"; const TodoList = () => { - const todos = useTodoStore((state) => state.todos); + const [todos] = useAtom(todosAtom); return ( 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/src/store/useTodoStore.js b/src/store/useTodoStore.js deleted file mode 100644 index 9d1c4fc..0000000 --- a/src/store/useTodoStore.js +++ /dev/null @@ -1,17 +0,0 @@ -import { create } from "zustand"; - -export const useTodoStore = create((set) => ({ - todos: [], - addTodo: (newTodo) => set((state) => ({ todos: [...state.todos, newTodo] })), - updateTodo: (id) => - set((state) => ({ - todos: state.todos.map((todo) => { - if (todo.id === id) { - return { ...todo, isDone: !todo.isDone }; - } - return todo; - }), - })), - deleteTodo: (id) => - set((state) => ({ todos: state.todos.filter((todo) => todo.id !== id) })), -})); diff --git a/yarn.lock b/yarn.lock index ce9e57d..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" From dff09919a89d498c31d3a6ce64f917f8d61a2e5a Mon Sep 17 00:00:00 2001 From: donghun Date: Wed, 26 Jul 2023 18:20:09 +0900 Subject: [PATCH 3/3] Update title --- src/App.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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로 만드는 투두리스트