본문 바로가기

React

React(2) - Login 버튼, comment(댓글) CR 구현

728x90
반응형

1) React

   1-1) Login 버튼

   1-2) comment(댓글) CR(Create, Read)

 

 

 

 

 

 

 

 

1) React

1-1) Login 버튼

이번 chapter에서 살펴볼 내용

  • ReactDOM
  • JSX
  • state
  • Component, Props
  • 생명주기
  • 조건부 rendering

JSX

// App Component가 있다고 가정함!
React.createElement(App, null, "hi~~~")

// div Element에 내용을 "hi~~~"로 넣기
React.createElement("div", null, "hi~~~")

// babel의 도움을 받아서 JSX
// React.createElement(App, null, "hi~~~") --> JSX
// React.createElement('Element 이름', '속성', 'innerHTML')
// 이때 "props"는 null(속성)과 "hi~~~"(innerHTML) 둘 다이다!
// 여기서 "hi~~~"는 "props.children"에 들어 있다!
// React.createElement(App, null, "hi~~~")
<APP>hi~~~</App>

// React.createElement("div", null, "hi~~~")

<div>hi~~~</div>

<div></div>
<div>Hello World~~!</div>

 

 

login_btn.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script
      crossorigin
      src="https://unpkg.com/react@18/umd/react.development.js"
    ></script>
    <script
      crossorigin
      src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"
    ></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
  </head>
  <body>
    <div id="root"></div>
    <script type="text/babel">
      class Word extends React.Component {
        constructor(props) {
          super(props);
        }

        render() {
          return <h2>{this.props.text}</h2>;
        }
      }

      class LoginText extends React.Component {
        constructor(props) {
          super(props);
        }

        // {this.state.isLogin ? "로그아웃" : "로그인"}
        render() {
          // 조건부 rendering
          return this.props.flag ? (
            <Word text="로그아웃" />
          ) : (
            <Word text="로그인" />
          );
        }
      }

      class LoginBtn extends React.Component {
        constructor(props) {
          super(props);
          this.state = {
            isLogin: false,
          };
          this.handleClick = this.handleClick.bind(this);
        }

        handleClick() {
          // console.log(this);
          this.setState({ isLogin: !this.state.isLogin });
        }

        render() {
          return (
            <button onClick={this.handleClick}>
              <LoginText flag={this.state.isLogin} />
            </button>
          );
        }
      }

      // component를 만들 시 첫글자는 반드시 대문자여야 한다는 규칙이 있다!
      // App Component: 일반적으로 크게 감싸고 시작하는 Component
      class App extends React.Component {
        constructor(props) {
          super(props);
          // children
          // property

          // props.children
          console.log(props);

          this.state = {
            isLogin: false,
          };
        }

        render() {
          return (
            <div>
              <Word text="hello world~~@" />
              <LoginBtn />
            </div>
          );
        }
      }
      const root = ReactDOM.createRoot(document.querySelector("#root"));
      // root.render(<div>Hello World!</div>);
      root.render(<App />);
      // root.render(
      //   <App id="hello" onClick="asdf">
      //     hi~~~
      //   </App>
      // );
    </script>
  </body>
</html>

 

데이터가 한 방향으로만 흘러간다는 것이 React의 큰 단점 중 하나이다!

 

 

 

 

1-2) comment(댓글) CR(Create, Read)

 

comment 구현 사전작업 : Component 구조 정리

 

 

comment.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script
      crossorigin
      src="https://unpkg.com/react@18/umd/react.development.js"
    ></script>
    <script
      crossorigin
      src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"
    ></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <link rel="stylesheet" href="./comment.css" />
  </head>
  <body>
    <div id="root"></div>
    <script type="text/babel">
      class CommentItem extends React.Component {
        render() {
          return (
            <ul className="comment-row">
              <li className="comment-id">{this.props.userid}</li>
              <li className="comment-content">{this.props.content}</li>
              <li className="comment-date">{this.props.date}</li>
            </ul>
          );
        }
      }

      class CommentForm extends React.Component {
        constructor(props) {
          super(props);
          // console.log(this.props.create);
          this.submitHandler = this.submitHandler.bind(this);
          this.changeHandler = this.changeHandler.bind(this);
          this.state = {
            value: "",
          };
        }

        changeHandler(e) {
          const { value } = e.target;

          this.setState({
            ...this.state,
            value,
          });

          // this.setState({
          //   ...this.state,
          //   value: e.target.value,
          // });
        }

        submitHandler(e) {
          e.preventDefault();
          this.props.create(this.state.value);
          this.setState({ value: "" });

          // this.props.create(e.target.commentInput.value) --> 여기서 commentInput은 아래 "ps_box" class명을 가진 span 태그 내의 input 태그의 id명을 "commentInput"으로 할 경우 사용 가능한 코드
          // e.target.reset();
          // e.target.commentInput.focus();
        }

        render() {
          return (
            <li className="comment-form">
              <form onSubmit={this.submitHandler}>
                <h4>
                  댓글쓰기 <span>({this.props.length})</span>
                </h4>
                <span className="ps_box">
                  <input
                    type="text"
                    onChange={this.changeHandler}
                    className="int"
                    value={this.state.value}
                    placeholder="댓글 내용을 입력해주세요!"
                  />
                </span>
                <input type="submit" value="등록" className="btn" />
              </form>
            </li>
          );
        }
      }

      class CommentList extends React.Component {
        // this.props.items의 데이터 타입 : Array(배열)
        loop(v, k) {
          return (
            <CommentItem
              key={k}
              userid="web7722"
              content={v.content}
              date="2023-02-22"
            />
          );
        }

        render() {
          return <li>{this.props.items.map(this.loop)}</li>;
        }
      }

      class Comment extends React.Component {
        constructor(props) {
          super(props);
          this.state = {
            comment: [
              {
                userid: "web7722",
                content: "hi comment1",
                date: "2023-02-22",
              },
              {
                userid: "web7722",
                content: "hi comment2",
                date: "2023-02-22",
              },
              {
                userid: "web7722",
                content: "hi comment3",
                date: "2023-02-22",
              },
            ],
          };

          // comment data의 형태가 "[{userid: 'aa', content: 'asdfg', date: '2023-02-24'}, {}, {}]" 이렇게 돼야 저장하기 용이하다!
          this.create = this.create.bind(this);
        }

        // props로 값을 넘길 수 있음
        // comment에서 자기 자신의 상태를 바꿀 수 있는 함수를 만듦
        create(content) {
          // setState: state(상태)를 바꾸겠다는 메서드
          this.setState({
            comment: [
              { userid: "web7722", content, date: "2023-02-22" },
              ...this.state.comment,
            ],
          });
        }

        render() {
          return (
            // "class"라는 예약어가 이미 있기 때문에 class 명을 지정할 때는 "className"을 사용해야 한다!
            <ul className="comment">
              <CommentForm
                create={this.create}
                length={this.state.comment.length}
              />
              <CommentList items={this.state.comment} />
            </ul>
          );
        }
      }

      class App extends React.Component {
        render() {
          return (
            <div>
              <Comment />
              <Comment />
              <Comment />
            </div>
          );
        }
      }

      const root = ReactDOM.createRoot(document.querySelector("#root"));
      root.render(<App />);
    </script>
  </body>
</html>

 

 

comment.css

* {
  margin: 0;
  padding: 0;
}

body {
  font-family: "Noto Sans KR", sans-serif;
  font-weight: 300;
}

ul,
li {
  list-style: none;
}

.comment {
  display: flex;
  flex-direction: column;
  flex-wrap: nowrap;
  padding: 30px;
  width: 600px;
  margin: 0 auto;
}

.comment > li {
  margin-top: 20px;
}

.comment > li:nth-child(1) {
  margin: 0px;
}

.comment-row {
  display: flex;
  justify-content: space-between;
  flex-direction: row;
}

.comment-row {
  margin-top: 20px;
  width: 100%;
}

.comment-row > li:nth-child(2) {
  flex-shrink: 0;
  flex-grow: 1;
  padding-left: 25px;
  z-index: 1;
  width: 100%;
}

.comment-row > li:nth-child(2) {
  width: 85px;
}

.comment-form > form {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  justify-content: space-between;
}

.comment-form > form > h4 {
  width: 100%;
  margin: 14px 0 14px 0;
}

.comment-content {
  word-break: break-all;
  padding-right: 25px;
}

.ps_box {
  display: block;
  position: relative;
  width: 80%;
  height: 51px;
  border: solid 1px #dadada;
  padding: 10px 14px 10px 14px;
  background: #fff;
  box-sizing: border-box;
}

.ps_box > input {
  outline: none;
}

.int {
  display: block;
  position: relative;
  width: 100%;
  height: 29px;
  padding-right: 25px;
  line-height: 29px;
  border: none;
  background: #fff;
  font-size: 15px;
  box-sizing: border-box;
  z-index: 10;
}

.btn {
  width: 18%;
  padding: 18px 0 16px;
  text-align: center;
  box-sizing: border-box;
  text-decoration: none;
  border: none;
  background: #333;
  color: #fff;
  font-size: 14px;
}

.comment-delete-btn {
  display: inline-block;
  margin-left: 7px;
  cursor: pointer;
}

.comment-update-input {
  border: none;
  border-bottom: 1px solid #333;
  font-size: 16px;
  color: #666;
  outline: none;
}