필요한 기능
1. 날짜, 주제 선택
2. 목표 추가 버튼 누르면 목표가 리스트로 화면에 출력 + 수정 가능
3. 일기 작성
4. 모두 선택 및 작성 완료 후 작성완료 버튼을 누르면 데이터에 추가되며 홈화면으로 돌아감.
4. 작성 취소를 누르면 작성 내용 모두 리셋되고 홈화면으로 돌아감
다이어리에는 글 새로 작성과 수정이 있는데, 모두 똑같은 form을 가지고 있고 수정의 경우 초기 데이터값과 전달되는 함수만 다른 것이다.
그러니 Editor 컴포넌트로 분리하여 두 페이지에서 모두 사용할 수 있다.
1. 날짜 선택
// 날짜 형식 변환
const getStringDate = (date) => {
return date.toISOString().slice(0, 10)
}
const DiaryEditor =() => {
// 입력되는 date를 관리할 state (초기값은 오늘 날짜)
const [date, setDate] = useState(getStringDate(new Date));
// 이때, 초기값에 그냥 new Date로만 하면 형식이 맞지 않아 초기값 설정이 되지 않는다.
// console.log(new Date) : Mon Aug 07 2023 03:33:14 GMT+0900 (한국 표준시)
// 날짜의 형식을 변환하는 함수를 만들어 이용해야 한다.
// console.log(getStringDate(new Date)) : 2023-08-06
return
<span>날짜 : </span>
{/* value의 초기값은 date의 값, onChange로 setDate */}
<input value = {date}
onChange={(e)=> setDate(e.target.value)}
type="date" />
}
이때 날짜를 변환하는 함수는 앱 여기저기서 쓰일 수 있기 때문에 따로 함수를 분리하여 사용하면 편리하다.
// date.js
export const getStringDate = (date) => {
return date.toISOString().slice(0, 10)
}
// DiaryEditor.js에서 import하여 사용
import {getStringDate} from "../util/date"
2. 주제 선택
주제를 선택하였을 때 어떤 주제가 선택되었는지 위해 subjectList와 SubjectItem을 각각 컴포넌트로 분리하여 사용한다.
// SubjectList.js
export const subjectList = [
{
subject_id : 1,
subject_img : process.env.PUBLIC_URL + `/assets/daily.png`,
subject_name : "일상"
},
{
subject_id : 2,
subject_img : process.env.PUBLIC_URL + `/assets/study.png`,
subject_name : "공부"
},
{
subject_id : 3,
subject_img : process.env.PUBLIC_URL + `/assets/workout.png`,
subject_name : "운동"
},
{
subject_id : 4,
subject_img : process.env.PUBLIC_URL + `/assets/saving.png`,
subject_name : "저축"
},
]
// SubjectItem.js
const SubjectItem = ({subject_id, subject_img, subject_name, onClick}) => {
return (
// 클릭되었을 때 전달할 props을 onClick으로 설정하고,
// 해당 아이템이 클릭되었을 때(onClick =) onClick prop을 이용하여 id를 전달한다.
<div className="editor_subject_item" onClick={()=> onClick(subject_id)}>
<img src={`${subject_img}`} />
<span>{subject_name}</span>
</div>
)
}
export default SubjectItem;
DiaryEditor에서 위의 컴포넌트를 사용할 수 있다.
const DiaryEditor =() => {
// 작성된 주제를 관리할 State (초기값 : 1(일상))
const [subject, setSubject] = useState(1);
return
<div className="editor_subject_wrapper">
<span>주제 : </span>
{subjectList.map((it)=>
<SubjectItem key={it.subject_id} {...it}
onClick={handleSubject}
)}
</div>
}
여기까지하면 개발자툴에서 클릭된 주제가 state에 저장된 것을 알 수 있다.
그럼 ui 면에서 어떤 주제가 선택되었는지 확인하여야 하는데
// DiaryEditor.js
<div className="editor_subject_wrapper">
<span>주제 : </span>
{subjectList.map((it)=>
<SubjectItem key={it.subject_id} {...it}
onClick={handleSubject}
isSelected={it.subject_id === subject}/>
)}
</div>
// isSelected를 해당 item의 subject_id가 subject(id)와 같다면 true, 다르면 false를 반환한다.
// ex) 현재 2번을 선택했다면, 1, 3, 4번의 item은 false, 2번은 true를 반환
DiaryItem에서 이 true, fasle 값을 이용하여 선택되었을 때 className을 따로 주어 css를 설정하면 된다.
const SubjectItem = ({subject_id, subject_img, subject_name, onClick, isSelected}) => {
return (
<div className={["editor_subject_item", `editor_subject_item_${isSelected}`].join(" ")}
onClick={()=> onClick(subject_id)}>
// ...
)
}
3. 목표 추가
+ 버튼을 누르면 <li><input /><li> 가 추가되고 이를 삭제할 수도 있다.
const DiaryEditor =() => {
// 목표의 상태를 관리할 state (배열의 형태로 저장될 것이다.)
const [goal, setGoal] = useState([]);
// 목표 추가
// goal 배열을 복사한 뒤, 새로운 빈 배열을 추가하여 setGoal로 상태를 저장한다.
const addGoal = () => {
const goals = [...goal, []]
setGoal(goals)
}
// 목표 삭제
// goal 배열을 복사한 뒤, 해당 인덱스에 위치한 요소를 splice 메소드를 사용하여 삭제한 후 setGoal로 상태를 저장한다.
const deleteGoal = (index) => {
const deleteTarget = [...goal];
deleteTarget.splice(index,1);
setGoal(deleteTarget);
}
// 목표 입력 관리
// goal 배열을 복사한 뒤, 해당 인덱스에 위치한 요소를 사용자가 입력한 값으로 업데이트한 후, setGoal로 상태를 저장한다.
const handleGaol = (e, index) => {
const inputGaol = [...goal];
inputGaol[index] = e.target.value;
setGoal(inputGaol);
}
// goal 배열을 map으로 순회하며 목표를 <li> 형태로 표시한다.
// 각 리스트마다 <input>이 있고 이 <input>의 값이 변경될 때 handleGoal함수를 호출하고,
// 삭제 버튼(x)을 클릭할 때 deleteGoal함수를 호출한다.
return
<div className="editor_goals_area">
<ul className="goal_list">
{goal.map((goal, index)=> {
return ( <li key={index}>
<input value={goal}
onChange={(e) => handleGaol(e, index)}/>
<MyButton text={"X"} onClick={()=>deleteGoal(index)}/>
</li> )
})}
</ul>
</div>
}
모두 작성하고 작성완료 버튼을 누르면 onCreate 함수를 불러와 데이터를 전달하면 된다.
수정하기
새 글 작성과 form은 같지만 선택한 다이어리의 원래 데이터가 그 form 안에 있어야 한다.
edit 페이지 컴포넌트에서 다음과 같이 작성한다.
// EditDiary.js
const EditDiary = () => {
// 선택된 다이어리의 id 가져오기
const { id } = useParams();
// 사용할 data 가져오기
const diaryList = useContext(DiaryStateContext);
// Editor form에 들어갈 데이터 state
const [originData, setOriginData] = useState();
const navigate = useNavigate();
// 마운트되었을 때 id와 일치하는 데이터 가져오기
useEffect(()=> {
// diaryList에 데이터가 있을 경우, 데이터 중 현재 페이지의 id와 같은 다이어리를 배열에 담음
if(diaryList.length >=1) {
const targetDiary = diaryList.find((it)=> parseInt(it.id) === parseInt(id))
// 만약 targetDiary가 있다면 originData를 그것으로 설정하고 없다면 홈화면으로 돌아감
if(targetDiary) {
setOriginData(targetDiary);
} else {
navigate('/', {replace:true})
}
}
},[diaryList, id])
// originData가 있다면 DiaryEditor 컴포넌트를 렌더링 함.
// isEdit: diaryEditor가 새 글 작성인지 글 수정인지 확인.
// origindata: 현재 id의 데이터
return <div>
{originData && <DiaryEditor isEdit={true} originData={originData}/>}
</div>
}
prop으로 전달한 isEdit과 originData를 DiaryEditor에서 전달받아 사용한다.
const DiaryEditor =({isEdit, originData}) => {
}
'JS > React Project' 카테고리의 다른 글
📆 위클리 플래너 - 주제 (0) | 2023.09.07 |
---|---|
📆 위클리 플래너 - 첫화면, 로그인 (0) | 2023.09.05 |
🏃♀️ 목표 다이어리 만들기 - 홈화면 (0) | 2023.08.01 |
🏃♀️ 목표 다이어리 만들기 - 기획 및 기초 세팅 (0) | 2023.07.28 |
📝 메모앱 업그레이드 (0) | 2023.07.25 |