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 38e4f6a0409b64abfc60091e7c786785312a9eb5 Mon Sep 17 00:00:00 2001 From: donghun Date: Wed, 26 Jul 2023 18:20:29 +0900 Subject: [PATCH 2/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..557b2bf 100644 --- a/src/App.js +++ b/src/App.js @@ -6,7 +6,7 @@ import TodoList from "./components/TodoList"; const App = () => { return ( - 리덕스로 만드는 투두리스트 + zustand로 만드는 투두리스트 From f522a31756399ecaad7ce0a181864c58b1ef574e Mon Sep 17 00:00:00 2001 From: donghun Date: Mon, 11 Dec 2023 20:24:55 +0900 Subject: [PATCH 3/3] Add Async Get TodoList Logic on Zustand --- src/shared/Layout.jsx | 7 ++++++- src/store/useTodoStore.js | 11 +++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/shared/Layout.jsx b/src/shared/Layout.jsx index e991db7..7d28478 100644 --- a/src/shared/Layout.jsx +++ b/src/shared/Layout.jsx @@ -1,8 +1,13 @@ -import React from "react"; +import React, { useEffect } from "react"; import GlobalStyle from "../styles/GlobalStyle"; import { styled } from "styled-components"; +import { fetchTodos } from "../store/useTodoStore"; const Layout = ({ children }) => { + useEffect(() => { + fetchTodos(); + }, []); + return ( diff --git a/src/store/useTodoStore.js b/src/store/useTodoStore.js index 9d1c4fc..fde237f 100644 --- a/src/store/useTodoStore.js +++ b/src/store/useTodoStore.js @@ -2,6 +2,7 @@ import { create } from "zustand"; export const useTodoStore = create((set) => ({ todos: [], + setTodos: (todos) => set({ todos }), addTodo: (newTodo) => set((state) => ({ todos: [...state.todos, newTodo] })), updateTodo: (id) => set((state) => ({ @@ -15,3 +16,13 @@ export const useTodoStore = create((set) => ({ deleteTodo: (id) => set((state) => ({ todos: state.todos.filter((todo) => todo.id !== id) })), })); + +export const fetchTodos = async () => { + const response = await fetch( + "https://jsonplaceholder.typicode.com/todos?_limit=3" + ); + const todos = await response.json(); + useTodoStore + .getState() + .setTodos(todos.map((todo) => ({ ...todo, isDone: false }))); +};