https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API
저장공간 크기
localStorage > sessiongStorage > cookie
// App.js
function App() {
useEffect(() => {
localStorage.setItem("key", 10);
}, []);
...

localStorage.setItem값을 바꾸는 경우, 한 번 입력된 값은 localStorage에 계속 저장되어 있음

value로 객체 저장 시, JSON.stringify() 로 변환해줘야함
useEffect(() => {
localStorage.setItem("item1", 10);
localStorage.setItem("item2", "200");
localStorage.setItem("item3", JSON.stringify({ value: 30 }));
}, []);


useEffect(() => {
const item1 = localStorage.getItem("item1");
const item2 = localStorage.getItem("item2");
const item3 = localStorage.getItem("item3");
console.log(item1, item2, item3);
}, []);

useEffect(() => {
const item1 = parseInt(localStorage.getItem("item1"));
const item2 = localStorage.getItem("item2");
const item3 = JSON.parse(localStorage.getItem("item3"));
console.log({ item1, item2, item3 });
}, []);

const reducer = (state, action) => {
let newState = [];
switch (action.type) {
case "INIT": {
return action.data;
}
case "CREATE": {
newState = [action.data, ...state];
break;
}
case "REMOVE": {
newState = state.filter((it) => it.id !== action.targetId);
break;
}
case "EDIT": {
newState = state.map((it) =>
it.id === action.data.id ? { ...action.data } : it
);
break;
}
default:
return state;
}
/* 일기데이터의 변환은 모두 reducer에서 처리되기 때문에,
newState가 변경될때마다 reducer에서 localStorage로 저장하면됨 */
localStorage.setItem("diary", JSON.stringify(newState));
return newState;
};

// App.js
const reducer = (state, action) => {
let newState = [];
switch (action.type) {
case "INIT": {
return action.data;
}
case "CREATE": {
newState = [action.data, ...state];
break;
}
case "REMOVE": {
newState = state.filter((it) => it.id !== action.targetId);
break;
}
case "EDIT": {
newState = state.map((it) =>
it.id === action.data.id ? { ...action.data } : it
);
break;
}
default:
return state;
}
/* 일기데이터의 변환은 모두 reducer에서 처리되기 때문에,
newState가 변경될때마다 reducer에서 localStorage로 저장하면됨 */
localStorage.setItem("diary", JSON.stringify(newState));
return newState;
};
function App() {
useEffect(() => {
const localData = localStorage.getItem("diary");
if (localData) {
// 새로 생성할 dataId의 current값은 diary data에서 가져온 가장 높은 id의 +1을 해줘야 함 -> diaryList를 sort하여 가장 높은 값 가져오기
// id기준으로 내림차순으로 정렬함수 생성
const diaryList = JSON.parse(localData).sort(
(a, b) => parseInt(b.id) - parseInt(a.id)
);
dataId.current = parseInt(diaryList[0].id) + 1;
dispatch({ type: "INIT", data: diaryList });
}
}, []);
...
}
// DiaryEditor.js
const DiaryEditor = ({ isEdit, originData }) => {
const navigate = useNavigate();
const [date, setDate] = useState(getStringDate(new Date()));
const [slctEmotionId, setslctEmotionId] = useState(3);
const [content, setContent] = useState("");
const contentRef = useRef();
// onRemove import
const { onCreate, onEdit, onRemove } = useContext(DiaryDispatchContext);
const handleSubmit = () => {
if (content.length < 1) {
contentRef.current.focus();
return;
}
if (
window.confirm(
isEdit ? "일기를 수정할까요?" : "새로운 일기를 추가할까요?"
)
) {
if (!isEdit) {
onCreate(date, content, slctEmotionId);
} else {
onEdit(originData.id, date, content, slctEmotionId);
}
}
navigate("/", { replace: true });
};
// 일기 삭제 함수
const handleRemove = () => {
if (window.confirm("일기를 삭제하시겠습니까?")) {
onRemove(originData.id);
navigate("/", { replace: true });
}
};
...
return (
<div>
<MyHeader
headText={isEdit ? "일기 수정하기" : "새 일기쓰기"}
leftChild={
<MyButton text={"< 뒤로가기"} onClick={() => navigate(-1)}></MyButton>
}
// 오른쪽 버튼으로 삭제하기 추가
// 수정상태일때만 버튼 노출되도록 설정
rightChild={
isEdit && (
<MyButton
text={"삭제하기"}
type={"negative"}
onClick={handleRemove}
/>
)
}
)

import { useNavigate } from "react-router-dom";
import { useContext } from "react";
import { DiaryDispatchContext } from "./../App.js";
import MyButton from "./MyButton";
const DiaryItem = ({ id, emotion, content, date }) => {
const navigate = useNavigate();
const { onCreate, onEdit, onRemove } = useContext(DiaryDispatchContext);
const env = process.env;
env.PUBLIC_URL = env.PUBLIC_URL || "";
// 날짜 객체 형식 변경
const strDate = new Date(parseInt(date)).toLocaleDateString();
// 일기 삭제 함수
const handleRemove = () => {
if (window.confirm("선택한 일기를 삭제하시겠습니까?")) {
onRemove(id);
navigate("/", { replace: true });
}
};
return (
<div className="DiaryItem">
<div
className={[
"emotion_img_wrapper",
`emotion_img_wrapper_${emotion}`,
].join(" ")}
onClick={() => navigate(`/diary/${id}`)}
>
<img src={process.env.PUBLIC_URL + `assets/emotion${emotion}.png`} />
</div>
<div className="info_wrapper" onClick={() => navigate(`/diary/${id}`)}>
<div className="diary_date">{strDate}</div>
<div className="diary_content_preview">{content.slice(0, 25)}</div>
</div>
<div className="btn_wrapper">
<MyButton text={"삭제하기"} type={"negative"} onClick={handleRemove} />
<MyButton
text={"수정하기"}
onClick={() => navigate(`/edit/${id}`)}
></MyButton>
</div>
</div>
);
};
export default DiaryItem;
