javaScript/react

onChange 이벤트(+이벤트 버블링 막기)

부엉이사장 2024. 7. 23. 08:27

# 시작하면서

이전에 배운걸 복습겸 새로 다 짜봤고 뭐 디자인도 다르니 혼동을 안하길 바란다

솔까말 이 블로그 나만 보는거라..뭐..

 

# 일단 이렇게 생겼다.

<input type="text" onChange={handleTitle} />

인풋 태그에다가 onChange옵션을 달아주었고 그곳에 handleTitle함수를 지정해줬다.

 

예전에 vue를 사용할때는 input에다가 v-model을 사용했던것 같은데

react의 방식이 조금 더 복잡한 듯 하다.

컴포넌트는 비교적 쉬웠던만큼 데이터바인딩이나 인풋데이터값을 쓰는거는 더 어렵고 뭐 이런거겠지?

근데 vue reactive도 갖다쓴거니 react도 이런 메서드가 있지않을까 쉽기도 하고..

암튼 뭐 지금 배우는 입장에서는 아 그렇구나 하고 배울뿐

 

 

# 암튼 저게 뭐냐?

<input type="text" onChange={handleTitle} />

handleTitle함수를 onChange에 넣어줬는데 저 함수는 아래와 같이 생겼다.

  const handleTitle = (event) => {
    setTitle(event.target.value);
  };

handleTitle이라는 함수에는 매개변수를 받는데 event라는 매개변수를 받는다.

저기에 인풋값이 넣어진다?정도로만 알고 넘어가면 될것같다.

때문에 인풋값이 변경될때마다 handleTitle함수가 호출된다는 소리임.

그럼 setTitle로 title이라는 변수(state)를 수정하게 된다.

 

강의에서는 함수리터럴로

<input type="text" onChange={(e)=>{e.target.value}} />

이렇게 바로 넘겨줬따.

사실 아래 방법이 더 나은것 같다.

 

onChange의 event 첫번째 매개변수로 받는거에서 어떤 기능이 더 있는지 더 알아보면 좋을듯

 

암튼 그렇게 하고 

대충 title이라는 변수(state)를 태그 아무데나 넣어보면

v-model에서 했던것처럼 입력값을 바꾸면 똑같이 따라 바뀌는걸 확인 할 수 있다.

 

 

빌드 할 경우 어떻게 빌드한 코드가 튀어나오는지 모르겠지만 onChange로 보자면 계속 입력이 있을때마다 함수가 두번 호출되는거라 좀 무거울듯 하다.. vue는 뭔가 되게 가벼워보였는데 useState도 그렇고 react는 좀 무거워보임.

 

 

# 이걸로 글쓰기 기능을 만들어보자. (전체코드)

import './App.css';
import React, { useState } from 'react'; // useState를 임포트

function App() {
  const [posts, setPosts] = useState([
    {
      title: '강남역',
      date: '2024/07/01',
      likeCount: 0,
      content: '강남역 다녀왔어요',
    },
    {
      title: '회현역',
      date: '2024/07/02',
      likeCount: 0,
      content: '회현역 다녀왔어요',
    },
    {
      title: '고디역',
      date: '2024/07/03',
      likeCount: 0,
      content: '고디역 다녀왔어요',
    },
  ]);

  const [state, setState] = useState({
    isPostModal: false,
    postModalIndex: null,
    isWriteModal: false,
  });

  const openPostModal = (i) => {
    const copiedState = { ...state };
    copiedState.postModalIndex = i;
    copiedState.isPostModal = true;
    setState(copiedState);
  };

  const openWriteModal = (i) => {
    const copiedState = { ...state };
    copiedState.isWriteModal = true;
    setState(copiedState);
  };

  const clickLike = (i) => {
    const copiedPosts = [...posts];
    copiedPosts[i].likeCount++;
    setPosts(copiedPosts);
  };

  const deletePost = (i) => {
    const copiedPosts = [...posts];
    copiedPosts.splice(i, 1);
    setPosts(copiedPosts);
  };

  return (
    <div className="App">
      {state.isWriteModal ? (
        <WriteModal
          state={state}
          posts={posts}
          setState={setState}
          setPosts={setPosts}
        />
      ) : null}
      {state.isPostModal ? (
        <PostModal
          state={state}
          posts={posts}
          setState={setState}
          setPosts={setPosts}
        />
      ) : null}

      <nav className="nav-top">
        <p>무찌 앱</p>
        <p>#######</p>
      </nav>
      <div className="lists">
        {posts.map((list, i) => {
          return (
            <div className="list" key={i}>
              <h4
                onClick={() => {
                  openPostModal(i);
                }}
              >
                {list.title}
              </h4>
              <p className="date">{list.date}</p>
              <p
                className="likeBtn"
                onClick={() => {
                  clickLike(i);
                }}
              >
                따봉
              </p>
              <p>{list.likeCount}</p>
              <p
                className="deleteBtn"
                onClick={() => {
                  deletePost(i);
                }}
              >
                삭제
              </p>
            </div>
          );
        })}
      </div>
      <div className="appBtn">
        <p className="writeBtn" onClick={openWriteModal}>
          글쓰기
        </p>
      </div>
    </div>
  );
}

const PostModal = ({ posts, state, setState, setPosts }) => {
  const closeModal = () => {
    const copiedState = { ...state };
    copiedState.isPostModal = false;
    setState(copiedState);
  };

  const edit = () => {
    const copiedPosts = [...posts];
    const editedContent = prompt(
      '새로운 글내용',
      copiedPosts[state.postModalIndex].content,
    );
    if (editedContent) {
      copiedPosts[state.postModalIndex].content = editedContent;
      setPosts(copiedPosts);
    }
  };
  return (
    <div className="detailPost-back">
      <div className="detailPost">
        <div className="title">
          <h4>{posts[state.postModalIndex].title}</h4>
          <p>{posts[state.postModalIndex].date}</p>
        </div>
        <div className="inner-back">
          <div className="inner-front">
            <p>{posts[state.postModalIndex].content}</p>
          </div>
        </div>
        <div className="btn-box">
          <p className="btn" onClick={edit}>
            수정
          </p>
          <p className="btn" onClick={closeModal}>
            닫기
          </p>
        </div>
      </div>
    </div>
  );
};

const WriteModal = ({ posts, state, setState, setPosts }) => {
  const closeModal = () => {
    const copiedState = { ...state };
    copiedState.isWriteModal = false;
    setState(copiedState);
  };

  const [title, setTitle] = useState('');
  const [content, setContent] = useState('');

  const getCurrentDate = () => {
    const today = new Date();

    const year = today.getFullYear();
    const month = String(today.getMonth() + 1).padStart(2, '0'); // 월은 0부터 시작하므로 +1 필요
    const day = String(today.getDate()).padStart(2, '0');

    return `${year}/${month}/${day}`;
  };

  const write = () => {
    const copiedPosts = [...posts];
    const newPost = {};
    newPost.title = title;
    newPost.content = content;
    newPost.date = getCurrentDate();
    newPost.likeCount = 0;
    copiedPosts.unshift(newPost);
    if (newPost.title) {
      setPosts(copiedPosts);
      closeModal();
    } else {
      closeModal();
    }
  };

  const handleTitle = (event) => {
    setTitle(event.target.value);
  };

  const handleContent = (event) => {
    setContent(event.target.value);
  };

  return (
    <div className="write-back">
      {title}
      <div className="detailPost">
        <div className="title">
          <input type="text" onChange={handleTitle} />
        </div>
        <div className="inner-back">
          <div className="inner-front">
            <textarea onChange={handleContent}></textarea>
          </div>
        </div>
        <div className="btn-box">
          <p className="btn" onClick={write}>
            확인
          </p>
          <p className="btn" onClick={closeModal}>
            닫기
          </p>
        </div>
      </div>
    </div>
  );
};

export default App;

 

강의 듣기전에 미리만들어보니까 이렇게 됐다.

일단 모달이 글 클릭하면 띄워지는 모달 하나였는데 글쓰기 모달을 새로 만들어줬고

제목은 input, 내용은 textarea로 만들어줬다.

각 onChange 이벤트핸들러?를 연결해줬다.

그럼 handle머시기 함수를 호출해주는데 여기엔 set머시기 하면서 해당 input내용을

해당 데이터로 바꿔준다.

이 데이터를 저장해놨다가 확인 버튼 누르면 posts어레이에 해당 글 오브젝트가 새로 생긴다.

 

 

 

*주의 사항

<input/>

인풋태그 만들때 일반적으론 <input>을 사용했는데 react에서는 /로 반드시 끝내줘야한다.

 

+ 이벤트버블링 없애기

              <h4
                onClick={() => {
                  openPostModal(i);
                }}
              >

위에 게시글 제목 누르면 게시글 내용뜨는 모달 뜨게 만든 기능이 있다.

제목을 누르면 openPostModal이라는 함수가 호출되게 했는데

사실 처음에는 그냥 글 전체적인걸 누르면 함수 호출되게 하려고 헀다.

근데 문제가

저 박스 전체를 누르면 

해당 게시글 모달이 떠야하는데 

'따봉'버튼만 눌러도 저 모달이 뜬다.

따봉버튼만 눌러지게 하고 싶다면

              <p
                className="likeBtn"
                onClick={() => {
                  clickLike(i);
                }}
              >
                따봉
              </p>

해당 따봉버튼의 onClick 이벤트핸들러 함수 리터럴에

event.stopPropagation메서드도 호출되게 해버리면 된다.

            <div
              className="list"
              key={i}
              onClick={() => {
                openPostModal(i);
              }}
            >
              <h4>{list.title}</h4>
              <p className="date">{list.date}</p>
              <p
                className="likeBtn"
                onClick={(event) => {
                  event.stopPropagation();
                  clickLike(i);
                }}
              >
                따봉
              </p>
              <p>{list.likeCount}</p>
              <p
                className="deleteBtn"
                onClick={() => {
                  deletePost(i);
                }}
              >
                삭제
              </p>
            </div>

이렇게 다시 제목에만 있던 모달창 이벤트를 그냥 전체 박스로 다시 바꾸고

따봉버튼에 event.stopPropagation메서드도 호출되게 하면

원하듯이 이벤트버블링을 막아준다.

 

 

 

 

+ part1 최종정리

리액트 초급을 배우면서 대충 중요한점들을 적어보자면

  • useState로 변수관리를 하면 데이터바인딩이 된다.
  • useState를 바꾸려면 set머시기 함수를 호출하고 인수로 바뀌게 해줘야한다.(ㅈ같음)
  • set머시기 함수를 쓸때 객체나 어레이 요소하나 수정할떄는 보통 얕은복사를 해서 쓴다(스프레드문법)
  • 컴포넌트는 쉽다. 데이터넘겨주기도 간단하다.
  • 반복문은 map을 사용한다
  • onClick으로 클릭시 실행할 코드를 연결할 수 있다.
  • onClick에는 단순한 함수는 그냥 함수리터럴이 할당된 변수 넣어주면 되고 매개변수가 있는 함수면 부모함수로 한번 더 감싸서 변수를 넘겨주자.
  • onChange로 바뀌는 데이터를 관리할 수 있다.

 

머 더 있을수도?

사실 아직까진 vue를 사용해본 경험이 많아서 크게 어려운점이 없는듯.

여기까지 배운내용만으로도 간단한 어플하나 만들 수 있을것 같다.

'javaScript > react' 카테고리의 다른 글

이미지 파일 관리 + public파일 경로  (0) 2024.07.30
bootstrap 연동하기  (0) 2024.07.29
looooooooooop  (0) 2024.07.18
component + props  (0) 2024.07.18
onClick이벤트 사용하기. (prettier, eslint설정)  (0) 2024.07.17