본문 바로가기
next+ts

next+ts 폼관련 페이지 전체 코드 둘러보기(Component+container)

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

component

import { GetServerSideProps, NextPage } from "next";
import { useRouter } from "next/router";
import { CounterState } from "../../../store/types/state";
import { da, js } from "../../../type";

const Firstid = ({
  main,
  update,
}: {
  main: CounterState;
  update: () => void;
}) => {
  const { query } = useRouter();
  const id = query.id;

  return (
    <div>
      {main.data.map((res: da, index: number) => {
        if (res.idx == id) {
          return (
            <div key={index}>
              <div className="section">
                <h1 className="title" key={"title"}>
                  {res.text.title}
                </h1>
                <div className="time">
                  <div className="timetitle">약속 시간</div>
                  <div className="timecontent">
                    <div className="timedateinput">{res.text.time}</div>
                    <div className="timetimeinput">{res.text.date}</div>
                  </div>
                </div>
                <div className="mapinfo">
                  <div className="map">
                    <iframe
                      src="https://m.map.kakao.com/"
                      width="350"
                      height="550"
                      loading="lazy"
                    ></iframe>
                  </div>
                  <div className="mapdes">
                    지도에
                    <h3>&#34;{res.text.address}&#34;</h3>
                    검색해주세요
                  </div>
                </div>

                <div className="content">
                  <h3 className="contitle">계획</h3>
                  <div className="condes">{res.text.content}</div>
                </div>
                <div className="tags">
                  {res.text.tags?.map((res, index) => {
                    return (
                      <div key={`tags${index}`} className="tag">
                        {res}
                      </div>
                    );
                  })}
                </div>
                <div className="buttons">
                  <div className="reset">
                    <button onClick={update}>수정</button>
                  </div>
                  <div className="finish">
                    <button>완료</button>
                  </div>
                </div>
              </div>

              <style jsx>
                {`
                  .section {
                    display: flex;
                    align-items: center;
                    flex-direction: column;
                    min-height: 900px;
                    background-color: #f5b467;
                  }
                  .section > .title {
                    margin: 1%;
                    color: #196aff;
                  }
                  .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;
                    margin: 2%;
                  }
                  .section > .time > .timecontent > * {
                    font-size: 18px;
                    font-weight: bolder;
                  }
                  .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 > .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 > .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 Firstid;
container
import { NextPage } from "next";
import { useRouter } from "next/router";

import { useDispatch, useSelector } from "react-redux";
import Id from "../../../components/form/firstform/id";
import { RootState } from "../../../store/reducers";
import { CounterState } from "../../../store/types/state";
const FirstidContainer: NextPage = () => {
  const dispatch = useDispatch();
  const router = useRouter();

  const { main }: { main: CounterState } = useSelector(
    (state: RootState) => state
  );
  const update = () => {
    router.push(`/form/firstform/update/${router.query.id}`);
  };

  return (
    <div>
      <Id main={main} update={update} />
    </div>
  );
};

export default FirstidContainer;
 
const { main }: { main: CounterState } = useSelector(
    (state: RootState) => state
  );
이부분은 redux에서 데이터를 가져오는 부분이다
  const update = () => {
    router.push(`/form/firstform/update/${router.query.id}`);
  };
이부분은 수정 페이지로 이동하는  기능을 하게 됩니다.
router를 이용해서 페이지id를 가져와서 같은 id의update페이지로 이동하게 됩니다.

==============================================================================

component

import { NextPage } from "next";
import { MutableRefObject, RefObject } from "react";

const FirstForm = ({
  titleref,
  dateref,
  timeref,
  addressref,
  contentref,
  tagsref,
  finish,
  reset,
}: {
  titleref: RefObject<HTMLInputElement>;
  dateref: RefObject<HTMLInputElement>;
  timeref: RefObject<HTMLInputElement>;
  addressref: RefObject<HTMLInputElement>;
  contentref: RefObject<HTMLTextAreaElement>;
  tagsref: RefObject<HTMLInputElement>;
  finish: () => void;
  reset: () => void;
}) => {
  return (
    <div>
      <div className="section">
        <h1 className="title">
          <input
            className="titleinput"
            ref={titleref}
            placeholder="약속이름 작성"
          />
        </h1>
        <div className="time">
          <div className="timetitle">약속 시간</div>
          <div className="timecontent">
            <input type="date" ref={dateref} className="timedateinput" />
            <input type="time" 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;
            margin: 2%;
          }
          .section > .time > .timecontent > * {
            font-size: 18px;
            font-weight: bolder;
          }
          .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>
  );
};
export default FirstForm;

 

container
import { useRef } from "react";
import { useSelector } from "react-redux";
import { useDispatch } from "react-redux";

import Index from "../../../components/form/firstform/index";
import { dataadd } from "../../../store/actions/action";
import { RootState } from "../../../store/reducers";
import { CounterState } from "../../../store/types/state";
import { da } from "../../../type";
const FirstindexContainer = () => {
  const dispatch = useDispatch();
  const { main }: { main: CounterState } = useSelector(
    (state: RootState) => state
  );

  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 = () => {
    titleref.current!.value = "";
    dateref.current!.value = "";
    timeref.current!.value = "";
    addressref.current!.value = "";
    contentref.current!.value = "";
    tagsref.current!.value = "";
  };
  const finish: () => void = () => {
    console.log(main.data);
    const data: da | undefined = makedata();
    if (data != undefined) {
      dispatch(dataadd(data));
      if (main.common.login == "admin") {
        alert(
          `id를 기억하세요 id:${(
            parseInt(main.data[main.data.length - 1].idx) + 1
          ).toString()}`
        );
      }
      reset();
    } 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: (parseInt(main.data[main.data.length - 1].idx) + 1).toString(),
        currentform: "first",
        maker: main.common.login,
        text: {
          address: addressref.current?.value,
          content: contentref.current?.value,
          date: dateref.current?.value,
          tags: tagarr,
          time: timeref.current?.value,
          title: titleref.current?.value,
        },
      };
      return data;
    }
  };
  return (
    <div>
      <Index
        titleref={titleref}
        dateref={dateref}
        timeref={timeref}
        addressref={addressref}
        contentref={contentref}
        tagsref={tagsref}
        finish={finish}
        reset={reset}
      />
    </div>
  );
};

export default FirstindexContainer;
 const reset: () => void = () => {
    titleref.current!.value = "";
    dateref.current!.value = "";
    timeref.current!.value = "";
    addressref.current!.value = "";
    contentref.current!.value = "";
    tagsref.current!.value = "";
  };
함수실행시 input부분을 빈칸으로 비워주는 기능을하게 됩니다.
 const finish: () => void = () => {
    console.log(main.data);
    const data: da | undefined = makedata();
    if (data != undefined) {
      dispatch(dataadd(data));
      if (main.common.login == "admin") {
        alert(
          `id를 기억하세요 id:${(
            parseInt(main.data[main.data.length - 1].idx) + 1
          ).toString()}`
        );
      }
      reset();
    } else {
      alert("태그 제외 모든 빈칸을 넣어주세요");
    }
  };
makedata로 데이터를 만들고 login이 admin이면 비회원이므로 id를 기억해서 찾을수 있도록 했습니다.
또한 데이터가 비어있으면 작동이 하지 않도록 설정했습니다.
 
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: (parseInt(main.data[main.data.length - 1].idx) + 1).toString(),
        currentform: "first",
        maker: main.common.login,
        text: {
          address: addressref.current?.value,
          content: contentref.current?.value,
          date: dateref.current?.value,
          tags: tagarr,
          time: timeref.current?.value,
          title: titleref.current?.value,
        },
      };
      return data;
    }
  };

데이터를 생성하는 함수 입니다.

 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);
ref로 input부분에 데이터를 가져오거나 업데이트 가능하도록 했습니다.