diff --git a/src/LecueNote/components/CreateNote/CreateNote.style.ts b/src/LecueNote/components/CreateNote/CreateNote.style.ts deleted file mode 100644 index 28ce432b..00000000 --- a/src/LecueNote/components/CreateNote/CreateNote.style.ts +++ /dev/null @@ -1,10 +0,0 @@ -import styled from '@emotion/styled'; - -export const Wrapper = styled.section` - display: flex; - gap: 3.2rem; - flex-direction: column; - - width: 100%; - margin: 7.8rem 0 3.3rem; -`; diff --git a/src/LecueNote/components/CreateNote/index.tsx b/src/LecueNote/components/CreateNote/index.tsx deleted file mode 100644 index 9aaeb42f..00000000 --- a/src/LecueNote/components/CreateNote/index.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { CreateNoteProps } from '../../type/lecueNoteType'; -import SelectColor from '../SelectColor'; -import WriteNote from '../WriteNote'; -import * as S from './CreateNote.style'; - -function CreateNote({ - clickedCategory, - clickedBgColor, - clickedTextColor, - isIconClicked, - contents, - setFileName, - handleChangeFn, - handleClickCategory, - handleClickedColorBtn, - handleClickedIcon, - imgFile, - uploadImage, - binaryImage, - setPresignedUrl, - selectedFile -}: CreateNoteProps) { - return ( - - - - - ); -} - -export default CreateNote; diff --git a/src/LecueNote/components/SelectColor/index.tsx b/src/LecueNote/components/SelectColor/index.tsx index a196c74c..eb2ccbc9 100644 --- a/src/LecueNote/components/SelectColor/index.tsx +++ b/src/LecueNote/components/SelectColor/index.tsx @@ -1,3 +1,5 @@ +import React from 'react'; + import { BG_COLOR_CHART, CATEGORY, @@ -8,19 +10,17 @@ import ShowColorChart from '../ShowColorChart'; import * as S from './SelectColor.style'; function SelectColor({ + lecueNoteState, isIconClicked, - clickedCategory, - clickedTextColor, - clickedBgColor, - setPresignedUrl, - binaryImage, - setFileName, + presignedUrlDispatch, + handleTransformImgFile, handleCategoryFn, handleColorFn, handleIconFn, - uploadImage, - selectedFile + selectedFile, }: SelectColorProps) { + const { textColor, background, category } = lecueNoteState; + return ( @@ -29,7 +29,8 @@ function SelectColor({ {it} @@ -38,35 +39,18 @@ function SelectColor({ })} - {clickedCategory === '텍스트색' ? ( - - ) : ( - - )} + ); } -export default SelectColor; +export default React.memo(SelectColor); diff --git a/src/LecueNote/components/ShowColorChart/index.tsx b/src/LecueNote/components/ShowColorChart/index.tsx index 507db0e8..452bb58e 100644 --- a/src/LecueNote/components/ShowColorChart/index.tsx +++ b/src/LecueNote/components/ShowColorChart/index.tsx @@ -10,17 +10,14 @@ function ShowColorChart({ isIconClicked, colorChart, state, + handleTransformImgFile, + presignedUrlDispatch, selectedFile, - setPresignedUrl, - binaryImage, - setFileName, - uploadImage, handleFn, handleIconFn, }: ShowColorChartProps) { const imgRef = useRef(null); - // 여기 - useGetPresignedUrl(setPresignedUrl, setFileName); + useGetPresignedUrl({ presignedUrlDispatch }); const handleImageUpload = () => { const fileInput = imgRef.current; @@ -33,15 +30,18 @@ function ShowColorChart({ reader1.readAsDataURL(file); reader1.onloadend = () => { if (reader1.result !== null) { - uploadImage(reader1.result as string); + handleTransformImgFile(reader1.result as string); } }; // reader2: 파일을 ArrayBuffer로 읽어서 PUT 요청 수행 const reader2 = new FileReader(); reader2.readAsArrayBuffer(file); - binaryImage(reader2); - selectedFile(file); + // reader1의 비동기 작업이 완료된 후 수행(onloadend() 활용) + reader2.onloadend = () => { + handleTransformImgFile(reader2); + selectedFile(file); + }; } }; @@ -72,6 +72,7 @@ function ShowColorChart({ - {nickname} - + {nickname} + {dateArr[0]}.{dateArr[1]}.{dateArr[2]} diff --git a/src/LecueNote/hooks/useGetPresignedUrl.ts b/src/LecueNote/hooks/useGetPresignedUrl.ts index 573b8471..cc859e8d 100644 --- a/src/LecueNote/hooks/useGetPresignedUrl.ts +++ b/src/LecueNote/hooks/useGetPresignedUrl.ts @@ -1,26 +1,37 @@ -import { useQuery } from 'react-query'; +import { useEffect, useRef } from 'react'; import { useNavigate } from 'react-router-dom'; import getPresignedUrl from '../api/getPresignedUrl'; +import { getPresignedUrlProps } from '../type/lecueNoteType'; -const useGetPresignedUrl = ( - setPresignedUrl: React.Dispatch>, - setFileName: React.Dispatch>, -) => { +const useGetPresignedUrl = ({ presignedUrlDispatch }: getPresignedUrlProps) => { const navigate = useNavigate(); + const isUnmounted = useRef(false); - const { isLoading, data } = useQuery({ - queryKey: ['get-presigned-url'], - queryFn: () => getPresignedUrl(), - onError: () => navigate('/error'), - onSuccess: (data) => { - setPresignedUrl(data.data.url); - setFileName(data.data.fileName); - }, - refetchOnWindowFocus: false, - }); + useEffect(() => { + isUnmounted.current = false; + const fetchData = async () => { + try { + const { data } = await getPresignedUrl(); - return { isLoading, data }; + presignedUrlDispatch({ + type: 'SET_PRESIGNED_URL', + presignedUrl: data.url, + filename: data.fileName, + }); + } catch (error) { + navigate('/error'); + } + }; + + if (!isUnmounted.current) { + fetchData(); + } + + return () => { + isUnmounted.current = true; + }; + }, []); }; export default useGetPresignedUrl; diff --git a/src/LecueNote/page/LeceuNotePage/LecueNotePage.style.ts b/src/LecueNote/page/LeceuNotePage/LecueNotePage.style.ts index d65164c2..a7bbbf47 100644 --- a/src/LecueNote/page/LeceuNotePage/LecueNotePage.style.ts +++ b/src/LecueNote/page/LeceuNotePage/LecueNotePage.style.ts @@ -11,3 +11,12 @@ export const Wrapper = styled.div` height: 100dvh; padding: 0 1.7rem; `; + +export const CreateNote = styled.section` + display: flex; + gap: 3.2rem; + flex-direction: column; + + width: 100%; + margin: 7.8rem 0 3.3rem; +`; diff --git a/src/LecueNote/page/LeceuNotePage/index.tsx b/src/LecueNote/page/LeceuNotePage/index.tsx index 2d01a324..7e21a0d1 100644 --- a/src/LecueNote/page/LeceuNotePage/index.tsx +++ b/src/LecueNote/page/LeceuNotePage/index.tsx @@ -1,11 +1,12 @@ -import { useState } from 'react'; +import { useReducer, useState } from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; import Header from '../../../components/common/Header'; import LoadingPage from '../../../components/common/LoadingPage'; import CommonModal from '../../../components/common/Modal/CommonModal'; -import CreateNote from '../../components/CreateNote'; import Footer from '../../components/Footer'; +import SelectColor from '../../components/SelectColor'; +import WriteNote from '../../components/WriteNote'; import { BG_COLOR_CHART, CATEGORY, @@ -13,41 +14,40 @@ import { } from '../../constants/colorChart'; import usePostLecueNote from '../../hooks/usePostLecueNote'; import usePutPresignedUrl from '../../hooks/usePutPresignedUrl'; +import { reducer } from '../../reducer/lecueNoteReducer'; import * as S from './LecueNotePage.style'; function LecueNotePage() { const MAX_LENGTH = 1000; const navigate = useNavigate(); - - const [contents, setContents] = useState(''); - const [imgFile, setImgFile] = useState(''); - const [imgFile2, setImgFile2] = useState(); - const [clickedCategory, setClickedCategory] = useState(CATEGORY[0]); - const [clickedTextColor, setClickedTextColor] = useState(TEXT_COLOR_CHART[0]); - const [clickedBgColor, setClickedBgColor] = useState(BG_COLOR_CHART[0]); - const [isIconClicked, setIsIconClicked] = useState(false); - const [fileName, setFileName] = useState(BG_COLOR_CHART[0]); - const [presignedUrl, setPresignedUrl] = useState(''); - const [file, setFile] = useState(); - const [modalOn, setModalOn] = useState(false); - const [escapeModal, setEscapeModal] = useState(false); - + const location = useLocation(); const putMutation = usePutPresignedUrl(); const postMutation = usePostLecueNote(); - const location = useLocation(); - const { bookId } = location.state || {}; - const handleClickCategory = ( - e: React.MouseEvent, - ) => { - setClickedCategory(e.currentTarget.innerHTML); - }; + const [modalOn, setModalOn] = useState(false); + const [escapeModal, setEscapeModal] = useState(false); + + const [lecueNoteState, dispatch] = useReducer(reducer, { + presignedUrl: '', + filename: BG_COLOR_CHART[0], + contents: '', + category: CATEGORY[0], + textColor: TEXT_COLOR_CHART[0], + background: BG_COLOR_CHART[0], + file: null, + isIconClicked: false, + imgToStr: '', + imgToBinary: new FileReader(), + }); const handleChangeContents = (e: React.ChangeEvent) => { - setContents(e.target.value); + dispatch({ type: 'SET_CONTENTS', contents: e.target.value }); if (e.target.value.length > MAX_LENGTH) { - setContents((e.target.value = e.target.value.substring(0, MAX_LENGTH))); + dispatch({ + type: 'SET_CONTENTS', + contents: (e.target.value = e.target.value.substring(0, MAX_LENGTH)), + }); alert('1000자 내로 작성해주세요.'); } }; @@ -55,34 +55,38 @@ function LecueNotePage() { const handleClickedColorBtn = ( e: React.MouseEvent, ) => { - if (clickedCategory === '텍스트색') { - setClickedTextColor(e.currentTarget.id); - } else { - setClickedBgColor(e.currentTarget.id); - setIsIconClicked(false); - } + e.currentTarget.name === 'textColor' + ? dispatch({ type: 'CLICKED_TEXT_COLOR', color: e.currentTarget.id }) + : dispatch({ type: 'CLICKED_BG_COLOR', color: e.currentTarget.id }); + + lecueNoteState.category !== '텍스트색' && + dispatch({ type: 'NOT_CLICKED_IMG_ICON' }); }; - const handleClickedIcon = () => { - setIsIconClicked(true); + const handleTransformImgFile = (file: string | FileReader) => { + if (typeof file === 'string') { + dispatch({ type: 'IMG_TO_STR', imgFile: file }); + } else { + dispatch({ type: 'IMG_TO_BINARY', imgFile: file }); + } }; const handleClickCompleteModal = async () => { - if (imgFile2) { - if (imgFile2.result && file) { + if (lecueNoteState.imgToBinary) { + if (lecueNoteState.imgToBinary.result && lecueNoteState.file) { putMutation.mutate({ - presignedUrl: presignedUrl, - binaryFile: imgFile2.result, - fileType: file.type, + presignedUrl: lecueNoteState.presignedUrl, + binaryFile: lecueNoteState.imgToBinary.result, + fileType: lecueNoteState.file.type, }); } } postMutation.mutate({ - contents: contents, - color: clickedTextColor, - fileName: fileName, - bgColor: clickedBgColor, - isIconClicked: isIconClicked, + contents: lecueNoteState.contents, + color: lecueNoteState.textColor, + fileName: lecueNoteState.filename, + bgColor: lecueNoteState.background, + isIconClicked: lecueNoteState.isIconClicked, bookId: bookId, }); }; @@ -110,24 +114,35 @@ function LecueNotePage() { headerTitle="레큐노트 작성" handleFn={() => setEscapeModal(true)} /> - setImgFile(file)} - setFileName={setFileName} - handleChangeFn={handleChangeContents} - handleClickCategory={handleClickCategory} - handleClickedColorBtn={handleClickedColorBtn} - handleClickedIcon={handleClickedIcon} - setPresignedUrl={setPresignedUrl} - binaryImage={(file) => setImgFile2(file)} - selectedFile={(file) => setFile(file)} - /> -