본문 바로가기
next+ts

next+ts 업데이터 페이지 전체 코드 둘러보기 (Component+container)

by 멈추지않아 2022. 5. 2.

component

import { RefObject } from "react";
import { CounterState } from "../../../../store/types/state";
import { da } from "../../../../type";

const Modify = ({
  titleref,
  dateref,
  timeref,
  addressref,
  contentref,
  tagsref,
  finish,
  reset,
  main,
  id,
}: {
  titleref: RefObject<HTMLInputElement>;
  dateref: RefObject<HTMLInputElement>;
  timeref: RefObject<HTMLInputElement>;
  addressref: RefObject<HTMLInputElement>;
  contentref: RefObject<HTMLTextAreaElement>;
  tagsref: RefObject<HTMLInputElement>;
  finish: () => void;
  reset: () => void;
  main: CounterState;
  id: String | string[] | undefined;
}) => {
  return (
    <div>
      {main.data.map((res: da, index: number) => {
        if (res.idx == id) {
          return (
            <div key={index}>
              <div className="section">
                <h1 className="title">
                  <input
                    className="titleinput"
                    ref={titleref}
                    placeholder="약속이름 작성"
                  />
                </h1>
                <div className="time">
                  <div className="timetitle">약속 시간</div>
                  <div className="timecontent">
                    <p>Date:</p>
                    <input ref={dateref} className="timedateinput" />
                    <p>Time:</p>
                    <input ref={timeref} className="timetimeinput" />
                  </div>
                </div>
                <div className="mapinfo">
                  <div className="map">
                    <iframe
                      src="https://m.map.kakao.com/"
                      width="350"
                      height="350"
                      loading="lazy"
                    ></iframe>
                  </div>
                  <div className="mapdes">
                    지도에
                    <h3>
                      <input
                        ref={addressref}
                        className="mapdesinput"
                        placeholder="주소 또는 장소 이름 작성"
                      />
                    </h3>
                    검색해주세요
                  </div>
                </div>

                <div className="content">
                  <h3 className="contitle">계획</h3>
                  <div className="condes">
                    <textarea
                      ref={contentref}
                      className="condesinput"
                      placeholder="세부내용 작성"
                    />
                  </div>
                </div>
                <input
                  ref={tagsref}
                  className="tagsinput"
                  placeholder="#으로 태그를 구분해주세요"
                />
                <div className="tags">
                  <div className="tag">예시</div>
                  <div className="tag">예시</div>
                  <div className="tag">예시</div>
                </div>
                <div className="buttons">
                  <div className="reset">
                    <button onClick={reset}>초기화</button>
                  </div>
                  <div className="finish">
                    <button onClick={finish}>완료</button>
                  </div>
                </div>
              </div>
              <style jsx>
                {`
                  .section {
                    display: flex;
                    align-items: center;
                    flex-direction: column;
                    min-height: 900px;
                    background-color: #f5b467;
                    width: 100%;
                  }
                  .section > .title {
                    margin: 1%;
                    color: #196aff;
                  }
                  .section > .title > .titleinput {
                    font-size: 28px;
                    text-align: center;
                    font-weight: bolder;
                    width: 350px;
                  }
                  .scetion > .time {
                    width: 100%;
                  }
                  .section > .time > .timetitle {
                    font-size: 28px;
                    font-weight: bolder;
                    width: 350px;
                    text-align: center;
                    margin: 2%;
                    display: flex;
                    flex-direction: column;
                  }
                  .section > .time > .timetitle::after {
                    content: " ";
                    border: 1px solid #636beb;
                    width: 90%;
                    margin: auto;
                  }
                  .section > .time > .timecontent {
                    font-size: 28px;
                    font-weight: bolder;
                    width: 350px;
                    text-align: center;
                    display: flex;
                    justify-content: space-around;
                  }
                  .section > .time > .timecontent > * {
                    font-size: 18px;
                    font-weight: bolder;
                    max-width: 30%;
                  }
                  .section > .time > .timecontent > .timetimeinput {
                    font-size: 18px;
                    font-weight: bolder;
                    max-width: 30%;
                  }
                  .section > .time > .timecontent > .timtdateinput {
                    font-size: 18px;
                    font-weight: bolder;
                    max-width: 40%;
                  }
                  .section > .mapinfo {
                    border: 5px solid #636beb;
                    border-radius: 10px;
                    max-width: 354px;

                    margin: 1%;
                  }
                  .section > .mapinfo > .map {
                    background-color: whitesmoke;
                  }

                  .section > .mapinfo > .mapdes {
                    font-size: 20px;
                    text-align: center;
                    padding: 1%;

                    background-color: whitesmoke;
                  }
                  .section > .mapinfo > .mapdes > h3 > .mapdesinput {
                    font-size: 20px;
                    text-align: center;
                    font-weight: bolder;
                  }

                  .section > .content {
                    min-width: 350px;

                    display: flex;
                    flex-direction: column;
                    margin: 1%;
                    border: 3px solid #636beb;
                    background-color: #eab7f5;
                  }
                  .section > .content > .contitle {
                    text-align: center;
                    color: #196aff;
                    font-size: 28px;
                    width: 100%;
                    display: flex;
                    flex-direction: column;
                  }
                  .section > .content > .contitle::after {
                    content: " ";
                    border: 1px solid #636beb;
                    width: 80%;
                    margin: auto;
                  }
                  .section > .content > .condes {
                    text-align: center;
                    font-weight: bolder;
                    font-size: 18px;

                    width: 100%;
                  }
                  .section > .content > .condes > .condesinput {
                    text-align: center;
                    font-weight: bolder;
                    font-size: 18px;
                    margin: 1%;
                    width: 80%;
                    height: 100px;
                  }
                  .tagsinput {
                    width: 350px;
                    font-size: 18px;
                  }
                  .section > .tags {
                    width: 350px;
                    display: flex;
                    flex-flow: wrap;
                    justify-content: center;
                  }
                  .section > .tags > .tag {
                    border: 1px solid #636beb;
                    border-radius: 16px;
                    margin: 1%;
                    padding: 1%;
                    background-color: #636beb;
                    font-weight: bolder;
                    color: whitesmoke;
                  }
                  .section > .tags > .tag::before {
                    content: "#";
                  }
                  .section > .buttons {
                    display: flex;
                    justify-content: center;
                    width: 90%;
                    max-width: 350px;
                  }
                  .section > .buttons > * {
                    width: 200px;
                  }
                  .section > .buttons > * > * {
                    font-weight: bolder;
                    font-size: 28px;
                    margin: 5%;
                    max-width: 100%;
                    padding: 5%;

                    border-radius: 12px;
                  }
                  .section > .buttons > .finish {
                    display: flex;
                    justify-content: end;
                  }
                  .section > .buttons > .reset > * {
                    background-color: yellow;
                    border: 1px solid;
                  }
                  .section > .buttons > .finish > * {
                    background-color: #6856f8;
                    border: 1px solid;
                    display: flex;
                    justify-content: end;
                  }
                `}
              </style>
            </div>
          );
        }
      })}
    </div>
  );
};
export default Modify;
 
 
container
import { useRouter } from "next/router";
import { useEffect, useRef, useState } from "react";
import { useDispatch } from "react-redux";
import { useSelector } from "react-redux";
import Modify from "../../../../components/form/firstform/update/[id]";
import { dataupdate } from "../../../../store/actions/action";
import { CounterState, RootState } from "../../../../store/types/state";
import { da } from "../../../../type";
const ModifyContainer = () => {
  const dispatch = useDispatch();

  const { main }: { main: CounterState } = useSelector(
    (state: RootState) => state
  );
  const { query } = useRouter();
  const id: String | string[] | undefined = query.id;

  useEffect(() => {
    main.data.find((res) => {
      if (res.idx == id) {
        titleref.current!.value = res.text.title;
        dateref.current!.value = res.text.date;
        timeref.current!.value = res.text.time;
        addressref.current!.value = res.text.address;
        contentref.current!.value = res.text.content;
        tagsref.current!.value = "#" + res.text.tags?.join("#");

        return res;
      }
    });
  }, [main.data, id]);

  const titleref = useRef<HTMLInputElement>(null);
  const dateref = useRef<HTMLInputElement>(null);
  const timeref = useRef<HTMLInputElement>(null);
  const addressref = useRef<HTMLInputElement>(null);
  const contentref = useRef<HTMLTextAreaElement>(null);
  const tagsref = useRef<HTMLInputElement>(null);
  const reset: () => void = () => {
    main.data.find((res) => {
      if (res.idx == id) {
        titleref.current!.value = res.text.title;
        dateref.current!.value = res.text.date;
        timeref.current!.value = res.text.time;
        addressref.current!.value = res.text.address;
        contentref.current!.value = res.text.content;
        tagsref.current!.value = "#" + res.text.tags?.join("#");

        return res;
      }
    });
  };
  const finish: () => void = () => {
    const data: da | undefined = makedata();
    if (data != undefined) {
      dispatch(dataupdate(data));
      if (main.common.login == "admin") {
        alert(`id를 기억하세요 id:${id}`);
      }
    } else {
      alert("태그 제외 모든 빈칸을 넣어주세요");
    }
  };
  const makedata = () => {
    if (
      titleref.current?.value &&
      dateref.current?.value &&
      timeref.current?.value &&
      addressref.current?.value &&
      contentref.current?.value
    ) {
      const tagarr: string[] | undefined = tagsref.current?.value.split("#");
      tagarr?.splice(0, 1);

      const data: da = {
        idx: query.id!.toString(),
        currentform: "first",
        maker: main.common.login,
        text: {
          address: addressref.current?.value,
          content: contentref.current?.value,
          date: dateref.current?.value,
          tags: tagarr ? tagarr : [],
          time: timeref.current?.value,
          title: titleref.current?.value,
        },
      };
      return data;
    }
  };
  return (
    <div>
      <Modify
        main={main}
        titleref={titleref}
        dateref={dateref}
        timeref={timeref}
        addressref={addressref}
        contentref={contentref}
        tagsref={tagsref}
        finish={finish}
        reset={reset}
        id={id}
      />
    </div>
  );
};
export default ModifyContainer;

container 기준으로 설명을 하도록 해보겠습니다.
우선 uesEffect 부분 보면
  useEffect(() => {
    main.data.find((res) => {
      if (res.idx == id) {
        titleref.current!.value = res.text.title;
        dateref.current!.value = res.text.date;
        timeref.current!.value = res.text.time;
        addressref.current!.value = res.text.address;
        contentref.current!.value = res.text.content;
        tagsref.current!.value = "#" + res.text.tags?.join("#");

        return res;
      }
    });
  }, [main.data, id]);

여기서 랜더링 되기전에 기본 값을 넣어줘야 되기때문에 useref로 특정 html요소 정해주고 해당 요소의 value를 설정해주었습니다. 

tagarr은 join으로 중간에 #넣어주고 젤처음에는 따로#을 넣어주었습니다.

 

  const reset: () => void = () => {
    main.data.find((res) => {
      if (res.idx == id) {
        titleref.current!.value = res.text.title;
        dateref.current!.value = res.text.date;
        timeref.current!.value = res.text.time;
        addressref.current!.value = res.text.address;
        contentref.current!.value = res.text.content;
        tagsref.current!.value = "#" + res.text.tags?.join("#");

 

        return res;
      }
    });
  };
reset부분은 누르면 useeffect부분이랑 똑같은 기능을 실행하여 최초 상태로 만들어주게 됩니다.

 

const makedata = () => {
    if (
      titleref.current?.value &&
      dateref.current?.value &&
      timeref.current?.value &&
      addressref.current?.value &&
      contentref.current?.value
    ) {
      const tagarr: string[] | undefined = tagsref.current?.value.split("#");
      tagarr?.splice(0, 1);

 

      const data: da = {
        idx: query.id!.toString(),
        currentform: "first",
        maker: main.common.login,
        text: {
          address: addressref.current?.value,
          content: contentref.current?.value,
          date: dateref.current?.value,
          tags: tagarr ? tagarr : [],
          time: timeref.current?.value,
          title: titleref.current?.value,
        },
      };
      return data;
    }
  };
makedata는 입력된 데이터를 활용하여  da양식의 객체를 만들어주는 함수 입니다. route에서 해당 id를 받아와서  !로 notnull이라고 해주고 tostring으로 배열의 가능성을 없애줍니다.
그리고 tagarr은 없는경우 빈배열을 넣어주도록  세팅했습니다.

 

const finish: () => void = () => {
    const data: da | undefined = makedata();
    if (data != undefined) {
      dispatch(dataupdate(data));
      if (main.common.login == "admin") {
        alert(`id를 기억하세요 id:${id}`);
      }
    } else {
      alert("태그 제외 모든 빈칸을 넣어주세요");
    }
  };
finsih함수는  makedata로 만들어진 데이터를 가져와서 dispatch로 redux함수를 실행하고   data데이터가 undefined이면 빈칸없이 입력하라고 알림창을 뛰워주게 됩니다.
 const titleref = useRef<HTMLInputElement>(null);
  const dateref = useRef<HTMLInputElement>(null);
  const timeref = useRef<HTMLInputElement>(null);
  const addressref = useRef<HTMLInputElement>(null);
  const contentref = useRef<HTMLTextAreaElement>(null);
  const tagsref = useRef<HTMLInputElement>(null);
여기서 usestate말고 useRef쓴 이유가 궁금할수 있는데 검색해보니까 usestate는 값이변화할때마다 rerendering된다고 봤습니다. useref는 그 값만 변한다고 봐서 useref가 더 효율적이라고 생각했습니다.
 
그리고 제가 ***.**?.* 이런거 썼는데 이건 !를 붙이면 해당 요소가 null이 아니다 라는 뜻이고 ?는 null일수도 잇고 아닐수도 있다는 뜻입니다. makedata부분에 ?썼는데 !가 더 맞는 코드 같아요 ㅎㅎ 저대로 작성해놓은 이유는 저렇게하면 어떤오류가 날까 궁금해서 해봤는데 오류가 안나오네요 ㅎㅎ