TIL 28. 2024-02-05

이준구·2024년 2월 5일
0

TIL 순서

목록 보기
28/119
post-thumbnail

1. 헤더 UI 구현

function Header() {
    //Context API를 사용하여 전역 데이터 관리 
  const { isClick, setIsClick } = useContext(HomeContextProvider);
  
  //멤버 UI 형성
  const aespa = ["카리나", "지젤", "윈터", "닝닝"];
 
  //클릭한 멤버 
  const MemberClickEvent = (name) => {
    setIsClick(name);
  };

  return (
    <HeaderBackground>
      <HeaderTitle>에스파 팬레터 콜렉션</HeaderTitle>
      <HeaderList>
        {aespa.map((item) => (
          <HeaderItem
            key={item}
            onClick={() => MemberClickEvent(item)}
            $isClick={item === isClick}
          >
            {item}
          </HeaderItem>
        ))}
      </HeaderList>
    </HeaderBackground>
  );
}

export default Header;



2. 리스트 UI 구현

function List() {
  //Context API를 사용하여 전역 데이터 관리 
  const { data } = useContext(LatterContextProvider);
  const { isClick } = useContext(HomeContextProvider);
  
  //Header.jsx에서 클릭한 해당 멤버의 데이터
  const FilterData = data.filter((data) =>
    data.writedTo === isClick ? true : false
  );

  //데이터 존재 여부 확인
  const DataBoolean = FilterData.length !== 0 ? true : false;

  return (
    <FooterBackground>
      <FooterList>
        {DataBoolean ? (
          FilterData.map((data) => (
            <ListItem data={data} key={data.id}></ListItem>
          ))
        ) : (
          <DetailMemberName>
            {isClick
              ? `${isClick}의 첫 번째 팬레터의 주인공이 되세요!`
              : "멤버를 선택해야 팬레터를 볼 수 있습니다."}
          </DetailMemberName>
        )}
      </FooterList>
    </FooterBackground>
  );
}

export default List;

function ListItem({ data }) {
  const navigate = useNavigate();

  return (
    <FooterItem
      onClick={() => {
        navigate(`/detail/${data.id}`);
      }}
    >
      <FooterItemFigure>
        <FooterItemImage src={data.avatar}></FooterItemImage>
      </FooterItemFigure>
      <FooterItemTitle>
        <ListTitle>{data.nickname}</ListTitle>
        <ListDate>{data.createdAt}</ListDate>
      </FooterItemTitle>
      <FooterItemContent>{data.content}</FooterItemContent>
    </FooterItem>
  );
}

export default ListItem;

3. 입력 기능 구현

function Form() {
  //지역 상태 
  const [nickname, setNickname] = useState("");
  const [content, setContent] = useState("");
  const [writedTo, setWritedTo] = useState("");
  
  //Context API를 사용하여 전역 데이터 관리 
  const { setIsClick } = useContext(HomeContextProvider);
  const { setData } = useContext(LatterContextProvider);

  const date = new Date().toLocaleDateString("ko-KR", {
    year: "numeric",
    month: "long",
    day: "numeric",
    hour: "numeric",
    minute: "numeric",
  });

  //닉네임 입력
  const nicknameOnChangeHandler = (e) => setNickname(e.target.value);

  //내용 입력
  const contentOnChangeHandler = (e) => setContent(e.target.value);

  //멤버 선택
  const writedToOnChangeHandler = (e) => setWritedTo(e.target.value);

  //등록 버튼
  const submitEventHandler = (e) => {
    e.preventDefault();

    //유효성 검사
    if (nickname.trim() === "") {
      alert("닉네임을 입력해주세요");
      setNickname("");
    } else if (content.trim() === "") {
      alert("내용을 입력해주세요");
      setContent("");
    } else if (writedTo === "") {
      alert("멤버를 선택해주세요");
      setWritedTo("");
    } else {
      
      //데이터 추가 메소드
      DataAdd();

      //입력창 초기화
      setNickname("");
      setContent("");
      setWritedTo(""); 

      //List.jsx에 등록한 해당 멤버의 이름을 전달
      setIsClick(writedTo);
    }
  };


  const DataAdd = () => {
    setData((prev) => {
      return [
        {
          createdAt: date,
          nickname,
          avatar: profile,
          content,
          writedTo,
          id: prev.length + "1",
        }, 
        ...prev,
      ];
    });
  };

  return (
    <BodyForm onSubmit={submitEventHandler}>
      <BodyBox>
        <BodyLabel>닉네임:</BodyLabel>
        <BodyInputName
          value={nickname}
          onChange={nicknameOnChangeHandler}
          placeholder="최대 10글자까지 작성할 수 있습니다."
          maxLength={10}
        ></BodyInputName>
      </BodyBox>
      <BodyBox>
        <BodyLabel>내용:</BodyLabel>
        <BodyTextArea
          value={content}
          onChange={contentOnChangeHandler}
          placeholder="최대 100글자까지만 작성할 수 있습니다."
          maxLength={100}
        ></BodyTextArea>
      </BodyBox>
      <BodyBox>
        <BodyLabel>멤버 선택</BodyLabel>
        <select value={writedTo} onChange={writedToOnChangeHandler}>
          <option value={""}>멤버</option>
          <option value={"카리나"}>카리나</option>
          <option value={"지젤"}>지젤</option>
          <option value={"윈터"}>윈터</option>
          <option value={"닝닝"}>닝닝</option>
        </select>
      </BodyBox>
      <BodyRegister>
        <BodyRegisterButton type="submit">펜레터 등록</BodyRegisterButton>
      </BodyRegister>
    </BodyForm>
  );
}

export default Form;

4. 상세화면 UI 구현 및 수정, 삭제 기능

function DetailList() {
 
  //useParams()에서 받아온 데이터는 객체 형태이다.
  const { id } = useParams(); //Router에서 ":id" useParams를 통해 동적 매개변수 할당
  const navigate = useNavigate();
  
  
  //Context API를 사용하여 전역 데이터 관리 
  const { data, setData } = useContext(LatterContextProvider);
  
  //지역 상태 
  const [update, setUpdate] = useState(false); //글 수정 및 취소 버튼
  const [inputValue, setInputValue] = useState(detailData.content); //글 입력

  //해당 id와 일치한 데이터 정보
  const detailData = data.find((item) => item.id === id);

  //글 수정
  const onContentChange = (e) => setInputValue(e.target.value);

  //1) 수정 버튼 클릭 시 <textarea> 창으로 변경
  const UpdateOnClickEventHandler = () =>
    update ? textAreaButtonHandler() : setUpdate(!update);

  //2) <textarea>에서 수정 버튼 이벤트 핸들러
  const textAreaButtonHandler = () => {
    if (window.confirm("이대로 수정하시겠습니까?")) {
      //유효성 검사
      if (detailData.content === inputValue) {
        alert("변경된 내용이 없습니다.");
      } else {
        //데이터 수정
        setData((prev) => {
          //해당 id를 제외한 나머지 배열들
          const filterPrev = prev.filter((item) =>
            item.id !== id ? true : false
          );
          //해당 id 데이터의 내용을 수정
          detailData.content = inputValue;

          return (
            //수정된 내용을 맨 앞으로 이동
            [detailData, ...filterPrev]
          );
        });

        //수정 버튼 초기화
        setUpdate(!update);
        alert("수정되었습니다.");
        navigate("/");
      }
    }
  };

  //취소 버튼 이벤트 핸들러
  const CancelOnClickEventHandler = () => setUpdate(!update);

  //삭제 버튼 이벤트 핸들러
  const DeleteOnClickEventHandler = () => {
    if (window.confirm("정말 삭제하시겠습니까?")) {
      setData((prev) => prev.filter((item) => item.id !== id));
      alert("정상적으로 삭제되었습니다.");
      navigate("/");
    } else {
      alert("취소되었습니다.");
    }
  };

  return (
    <DetailBackground>
      <DetailHomeButton onClick={() => navigate("/")}>Home</DetailHomeButton>

      <DetailItemBox>
        <DetailHeader>
          <FooterItemFigure>
            <FooterItemImage src={detailData.avatar}></FooterItemImage>
          </FooterItemFigure>
          <DetailMemberName>{detailData.nickname}</DetailMemberName>
        </DetailHeader>
        <ListDate>{detailData.createdAt}</ListDate>
        <DetailMemberName>To: {detailData.writedTo}</DetailMemberName>
        {update ? (
          <DetailTextArea
            maxLength={200}
            value={inputValue}
            onChange={onContentChange}
          ></DetailTextArea>
        ) : (
          <DetailContent>{detailData.content}</DetailContent>
        )}

        <DetailButtonSection>
          <DetailButtonDiv>
            <DetailButton onClick={UpdateOnClickEventHandler}>
              수정
            </DetailButton>
          </DetailButtonDiv>
          <DetailButtonDiv>
            {update ? (
              <DetailButton onClick={CancelOnClickEventHandler}>
                취소
              </DetailButton>
            ) : (
              <DetailButton onClick={DeleteOnClickEventHandler}>
                삭제
              </DetailButton>
            )}
          </DetailButtonDiv>
        </DetailButtonSection>
      </DetailItemBox>
    </DetailBackground>
  );
}

export default DetailList;
profile
개발 중~~~ 내 자신도 발전 중😂🤣

0개의 댓글

관련 채용 정보