Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[3주차] 정대헌 미션 제출합니다. #21

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
73 changes: 16 additions & 57 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -1,73 +1,32 @@
import { React, useState, useReducer, useEffect } from "react";
import { FaBookReader } from "react-icons/fa";

import { GlobalProvider } from "./context/GlobalState";
import InputContainer from "./containers/InputContainer";
import ListItemContainer from "./containers/ListItemContainer";

//로컬 스토리지의 정보구성 {list: {item1, item2, ...}}

const initList = localStorage.getItem("list")
? JSON.parse(localStorage.getItem("list"))
: [];

const listReducer = (state, action) => {
let listBuffer = [];
//DELETE = 아이템 삭제, ADD = 아이템 추가, MODIFY = 아이템 type 수정(done/todo)
switch (action.type) {
case "DELETE":
listBuffer = state.filter((item) => item.id !== action.payload.id);
break;
case "ADD":
listBuffer = state.concat([action.payload]);
break;
case "MODIFY":
listBuffer = state.map((item) =>
item.id === action.payload.id ? action.payload : item
);
break;
default:
listBuffer = [...state];
break;
}
localStorage.setItem("list", JSON.stringify(listBuffer));
return [...listBuffer];
};

function App() {
// listState의 item obj는 다음의 형태를 지닌다 {id: "uniqueID", type: "done | todo", content: "item content"}
const [listState, dispatchListState] = useReducer(listReducer, initList);
const [listCnt, setListCnt] = useState({ todo: 0, done: 0 });

useEffect(() => {
setListCnt({
todo: listState.filter((item) => item.type === "todo").length,
done: listState.filter((item) => item.type === "done").length,
});
}, [listState, setListCnt]);

return (
<div className="background">
<div className="container">
<InputContainer dispatchListState={dispatchListState} />
{/* modType은 MODIFY 시 변경되는 type이다 */}
<ListItemContainer
title={"해야할 일"}
listState={listState}
listType={"todo"}
listCnt={listCnt.todo}
modType={"done"}
dispatchListState={dispatchListState}
/>
<ListItemContainer
title={"완료한 일"}
listState={listState}
listType={"done"}
listCnt={listCnt.done}
modType={"todo"}
dispatchListState={dispatchListState}
/>
<GlobalProvider>
<div className="background">
<div className="container">
<InputContainer />
{/* modType은 MODIFY 시 변경되는 type이다 */}
<ListItemContainer
title={"해야할 일"}
listType={"todo"} // 2: todoList
/>
<ListItemContainer
title={"완료한 일"}
listType={"done"} // -2: doneList
/>
</div>
</div>
</div>
</GlobalProvider>
);
}

Expand Down
32 changes: 9 additions & 23 deletions src/components/ListItem.js
Original file line number Diff line number Diff line change
@@ -1,46 +1,32 @@
import React from "react";
import React, { useContext } from "react";
import { FaTrash } from "react-icons/fa";

const ListItem = (props) => {
import { GlobalContext } from "../context/GlobalState";

const ListItem = ({ item }) => {
const { deleteItem, moveItem } = useContext(GlobalContext);

return (
<div className="item">
<div className="itemScrollArea">
<button
className="itemFinish"
type="button"
onClick={() => {
props.dispatchListState({
type: "MODIFY",
payload: {
id: props.item.id,
type: props.modType,
content: props.item.content,
},
});
moveItem(item.id);
}}
>
<p className="itemTitle">
{/* listType의 done/todo에 따라 del 태그 삽입 */}
{props.listType === "done" ? (
<del>{props.item.content}</del>
) : (
props.item.content
)}
{item.type === "done" ? <del>{item.content}</del> : item.content}
</p>
</button>
</div>
<button
className="itemDelete"
type="button"
onClick={() => {
props.dispatchListState({
type: "DELETE",
payload: {
id: props.item.id,
type: props.item.type,
content: props.item.content,
},
});
deleteItem(item.id);
}}
>
<p className="deleteIcon">
Expand Down
11 changes: 5 additions & 6 deletions src/containers/InputContainer.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import React, { useState } from "react";
import React, { useState, useContext } from "react";
import styled from "styled-components";

import { GlobalContext } from "../context/GlobalState";

// styled component를 사용한다

const AppInput = styled.div`
Expand Down Expand Up @@ -60,16 +62,13 @@ const SubmitBtn = styled.button`

const InputContainer = (props) => {
const [text, setText] = useState("");
const { addItem } = useContext(GlobalContext);

const submitHandler = (e) => {
e.preventDefault();
// form 내 입력이 존재하는 경우만 업데이트 한다
if (text !== "") {
const uid = new Date().getTime().toString();
props.dispatchListState({
type: "ADD",
payload: { id: uid, type: "todo", content: text },
});
addItem(uid, "todo", text);
setText("");
}
};
Expand Down
28 changes: 13 additions & 15 deletions src/containers/ListItemContainer.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,23 @@
import React from "react";
import React, { useContext } from "react";
import ListItem from "../components/ListItem";
import { GlobalContext } from "../context/GlobalState";

const ListItemContainer = ({ title, listType }) => {
const { listItems } = useContext(GlobalContext);

const ListItemContainer = (props) => {
return (
<section className="itemSection">
<h4 className="sectionTitle">
{props.title}(<p className="itemCount">{props.listCnt}</p>)
{title}(
<p className="itemCount">
{listItems.filter((item) => item.type === listType).length}
</p>
)
</h4>
<div className="itemList invisibleScrollbar">
{/* props.listType은 done/todo로 구분된다 */}
{props.listState.map((item) => {
if (item.type === props.listType) {
return (
<ListItem
key={item.id}
item={item}
listType={props.listType}
modType={props.modType}
dispatchListState={props.dispatchListState}
/>
);
{listItems.map((item) => {
if (item.type === listType) {
return <ListItem key={item.id} item={item} />;
}
return;
})}
Expand Down
21 changes: 21 additions & 0 deletions src/context/AppReducer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const AppReducer = (state, action) => {
//DELETE = 아이템 삭제, ADD = 아이템 추가, MODIFY = 아이템 type 수정(done/todo)
switch (action.type) {
case "DELETE":
return [...state.filter((item) => item.id !== action.payload.id)];
case "ADD":
return [...state.concat([action.payload])];
case "MODIFY":
return [
...state.map((item) =>
item.id === action.payload.id
? { ...item, type: item.type === "todo" ? "done" : "todo" }
: item
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

리듀서를 사용하니까 로직이 깔끔해지는 것 같습니다 ! 저도 적용해봐야겠군뇨

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

리듀서 너무 좋습니다 👍

),
];
default:
return [...state];
}
};

export default AppReducer;
55 changes: 55 additions & 0 deletions src/context/GlobalState.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import React, { createContext, useReducer, useEffect } from "react";
import AppReducer from "./AppReducer";

const initialItems = localStorage.getItem("list")
? JSON.parse(localStorage.getItem("list"))
: [];

export const GlobalContext = createContext(initialItems);

export const GlobalProvider = ({ children }) => {
const [listItems, dispatch] = useReducer(AppReducer, initialItems);

useEffect(() => {
localStorage.setItem("list", JSON.stringify(listItems));
}, [listItems]);

// Actions
const addItem = (uid, itemType, itemContent) => {
dispatch({
type: "ADD",
payload: { id: uid, type: itemType, content: itemContent },
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

uid는 유니크한 id를 생성하는 건가요?! 좋은 방법인 것 같습니다 배워갑니당,,

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

�아 그냥 변수 이름만 uid이고 사실 유니크 하진 않습니다, uuid를 사용해야하는 걸로 알고 있어요!

};

const moveItem = (uid) => {
dispatch({
type: "MODIFY",
payload: {
id: uid,
},
});
};

const deleteItem = (uid) => {
dispatch({
type: "DELETE",
payload: {
id: uid,
},
});
};

return (
<GlobalContext.Provider
value={{
listItems: listItems,
addItem,
moveItem,
deleteItem,
}}
>
{children}
</GlobalContext.Provider>
);
};