본문 바로가기

WINK-(Web & App)/HTML & CSS & JS 스터디

[2023 신입부원 심화 스터디] 김윤희 #마지막주차 React.js 섹션3 ~ 7 #치타는힘드러

반응형

섹션3. State 소개

1. state 소개

    props는 사용자가 컴포넌트를 사용하는 입장에서 중요

    state는 props의 값에 따라서 내부에 구현에 필요한 데이터들 

 

2. state의 사용

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      Subject: { title: "WEB", sub: "World wid Web!" },
    };
  }
  render() {
    return (
      <div ClassName="App">
        <Subject>
          title={this.state.Subject.title}
          sub={this.state.Subject.sub}
        </Subject>
        <Subject title="React" sub="For UI"></Subject>
        <TOC></TOC>
        <Content></Content>
      </div>
    );
  }
}

App.js가 내부적으로 사용하는 형태는 state를 통해 사용한다.

state값을 subject라는 컴포넌트의 props의 값으로 준다.

→상위 컴포넌트인 app의 상태를 하위컴포넌트로 전달하고 싶을 때, 상위 컴포넌트의 state 값을 하위 컴포넌트의 props의 값으로 전달하는 것은 가능하다.

 

3. key

TOC안에 있는 data를 app의 내부 state를 TOC에 주입하는 것을 통하여

TOC의 내부 데이터를 바뀌게 할 것

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      Subject: { title: "WEB", sub: "World wid Web!" },
      contents: [
        { id: 1, title: "HTML", desc: "HTML is for information" },
        { id: 2, title: "CSS", desc: "CSS is for design" },
        { id: 3, title: "JavaScript", desc: "JavaScript is for interactive" },
      ],
    };
  }
  render() {
    return (
      <div ClassName="App">
        <Subject>
          title={this.state.Subject.title}
          sub={this.state.Subject.sub}
        </Subject>
        <Subject title="React" sub="For UI"></Subject>
        <TOC data={this.state.contents}></TOC>
        <Content></Content>
      </div>
    );
  }
}
import React, { Component } from "react";
class TOC extends Component {
  render() {
    var lists = [];
    var data = this.props.data;
    var i = 0;
    while (i < data.length) {
      lists.push(
        <li key={data[i].id}>
          <a href={"/contenst/" + data[i].id}>{data[i].title}</a>
        </li>
      );
      i = i + 1;
    }
    return (
      <nav>
        <ul>{lists}</ul>
      </nav>
    );
  }
}

export default TOC;

→ TOC.js를 통하지 않고 내부 데이터의 목록을 바꿀 수 있음!→ <li key={data[i].id}>처럼 각각의 목록을 다른 것들과 구분할 수 있는 식별자를 추가해주어야 함.

  1. lists.push(<li><a href={"/contenst/" + data[i].id}>{data[i].title}</a></li>); 처럼 여러개의 element를 자동으로 생성하는 경우에는 key라고 하는 props를 가져야 한다는 에러 메세지가 발생.
  2. TOC안에 있는 data를 app의 내부 state를 TOC에 주입하는 것을 통하여 TOC의 내부 데이터를 바뀌게 할 것

 

 

섹션4. 이벤트 

1. 이벤트 

→ props, state, event가 서로 상호작용하면서 어플리케이션의 역동성을 부여

react에서는 props나 state의 값이 바뀌면 해당되는 컴포넌트의 render()함수가 호출되도록 약속되어있다.

→ props나 state가 바뀌면 화면이 다시 그려진다!

import React, { Component } from "react";
import TOC from "./components/TOC";
import Content from "./components/Content";
import Subject from "./components/Subject";
import "./App.css";

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      mode: "welcome",
      Subject: { title: "WEB", sub: "World wid Web!" },
      welcome: { title: "Welcom", desc: "Hello, React!!" },
      contents: [
        { id: 1, title: "HTML", desc: "HTML is for information" },
        { id: 2, title: "CSS", desc: "CSS is for design" },
        { id: 3, title: "JavaScript", desc: "JavaScript is for interactive" },
      ],
    };
  }
  render() {
    var _title,
      _desc = null;

    if (this.state.mode === "welcome") {
      _title = this.state.welcome.title;
      _desc = this.state.welcome.desc;
    } else if (this.state.mode === "read") {
      _title = this.state.contents[0].title;
      _desc = this.state.contents[0].desc;
    }
    return (
      <div ClassName="App">
        <Subject>
          title={this.state.Subject.title}
          sub={this.state.Subject.sub}
        </Subject>
        <Subject title="React" sub="For UI"></Subject>
        <TOC data={this.state.contents}></TOC>
        <Content title={_title} desc={_desc}></Content>
      </div>
    );
  }
}

export default App;

→ mode 값에 따라서 다른 render()함수가 호출되게 하도록!

 

2. 이벤트 설치 

Subject라는 컴포넌트를 풀어헤쳐 App에 도입 후 이벤트를 구현

import React, { Component } from "react";
import TOC from "./components/TOC";
import Content from "./components/Content";
import Subject from "./components/Subject";
import "./App.css";

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      mode: "welcome",
      Subject: { title: "WEB", sub: "World wid Web!" },
      welcome: { title: "Welcom", desc: "Hello, React!!" },
      contents: [
        { id: 1, title: "HTML", desc: "HTML is for information" },
        { id: 2, title: "CSS", desc: "CSS is for design" },
        { id: 3, title: "JavaScript", desc: "JavaScript is for interactive" },
      ],
    };
  }
  render() {
    var _title,
      _desc = null;

    if (this.state.mode === "welcome") {
      _title = this.state.welcome.title;
      _desc = this.state.welcome.desc;
    } else if (this.state.mode === "read") {
      _title = this.state.contents[0].title;
      _desc = this.state.contents[0].desc;
    }
    return (
      <div ClassName="App">
        {/* <Subject>
          title={this.state.Subject.title}
          sub={this.state.Subject.sub}
        </Subject> */}
        <header>
          <h1>
            <a
              href="/"
              onClick={function (e) {
                e.preventDefault(); //a태그의 기본적인 동작방법을 금지시킴 
              }}
            >
              {this.state.Subject.title}
            </a>
          </h1>
          {this.state.Subject.sub}
        </header>
        <Subject title="React" sub="For UI"></Subject>
        <TOC data={this.state.contents}></TOC>
        <Content title={_title} desc={_desc}></Content>
      </div>
    );
  }
}

export default App;

 

3. 이벤트에서 state 변경하기 

onClick={function (e) {
                e.preventDefault(); //a태그의 기본적인 동작방법을 금지시킴
                // this.state.mode = "welcome"; -> this의 값이 아무것도 셋팅되어 있지 않아!
                this.Setstate({
                  mode: "welcome",
                });
              }.bind(this)}

함수 안에서 this의 값은 아무것도 셋팅되어 있지 않아서 bind(this)코드를 추가해주어야 함.

또한 setState함수 사용!

 

4. 이벤트 bind함수 이해하기

render()함수에서 this는 컴포넌트 자신을 가리키는데, 함수 안에서 this는 아무것도 가리키지 않음.

this값을 주입하기 위해서 bind함수를 사용하는 것

bindTest함수에 bind함수를 또 호출한 후 bind함수의 첫번째 인자로 obj를 주게되면

bindTest함수의 this는 obj가 되어 새로운 함수(bindTest2)가 복제(?)되어 만들어진다.

이해가 안 가면 … 무조건 쓰세요 ~

 

5. 이벤트 setState함수 이해하기 

컴포넌트의 생성이 끝난 후에 동적으로 state의 값을 바꿀 때에는

this.state.mode = ‘welcome’;처럼 변경하면 안돼!

→ setState()함수를 통해 변경하고 싶은 객체를 전달하는 방식으로 변경해야함.

 

6. 컴포넌트 이벤트 만들기 1, 2, 3

이벤트를 만들어보는 생성자가 되어보기.

<Subject>
          title={this.state.Subject.title}
          sub={this.state.Subject.sub}
          onChangePage={" "}
          {function () {
            this.setState({ mode: "welcome" });
          }.bind(this)}
        </Subject>
<header>
        <h1>
          <a
            href="/"
            onClick={function (e) {
              e.preventDefalut();
              this.props.onChangePage();
            }.bind(this)}
          >
            {this.props.title}
          </a>
          {this.props.title}
        </h1>
        {this.props.sub}
      </header>

→ Subject컴포넌트에 onChangePage라는 이벤트를 만듦.

→ 그 이벤트에 함수를 설치해주면 이벤트가 발생되었을 때,

props로 전달된 onChagePage라는 함수를 호출하는 원리

 

e.target.dataset.id 를 통해 값을 추출!

 

섹션5. Create 기능 구현

1. 베이스캠프

redux는 데이터를 각각의 컴포넌트들에 분산해서 보관하는 것이 아니라,

하나의 데이터 저장소가 있어서, 그와 관련된 모든 컴포넌트들이 알아서 바뀌게 해줌!

 

2. Create 구현 소개 

CRUD : Create, Read, Update, Delete

import React, { Component } from "react";
class Control extends Component {
  render() {
    return (
      <ul>
        <li>
          <a
            href="/create"
            onClick={function (e) {
              e.preventDefault();
              this.props.onChangeMode("create");
            }.bind(this)}
          >
            create
          </a>{" "}
        </li>
        <li>
          <a
            href="/update"
            onClick={function (e) {
              e.preventDefault();
              this.props.onChangeMode("update");
            }.bind(this)}
          >
            update
          </a>
        </li>
        <li>
          <input
            onClick={function (e) {
              e.preventDefault();
              this.props.onChangeMode("delete");
            }.bind(this)}
            type="button"
            value="delete"
          ></input>
        </li>
      </ul>
    );
  }
}

export default Control;

Control.js만들기 !

 

4. mode 전환 기능 

→ App컴포넌트의 mode가 create가 될때, ReadContent가 CreateContent로 바뀜.

+ h2태그도 출력

//CreateContent.js
import React, { Component } from "react";

class CreateContent extends Component {
  render() {
    return (
      <article>
        <h2>Create</h2>
        <form></form>
      </article>
    );
  }
}

export default CreateContent;

 

5. form

<textarea></textarea> : 입력하려는 text가 여러줄일때

action : 데이터(사용자가 입력한 정보)를 어디에 전송할지

method=”post” : url에 노출이 안돼요

 

6. onSubmit 이벤트 

//CreateContent.js일
<form
          action="/create_process"
          method="post"
          onSubmit={function (e) {
            e.preventDefault();
            this.props.onSubmit(e.target.title.value, e.target.desc.value);
            alert("s");
          }.bind(this)}
        >

 

7. contents변경

						// this.state.contents.push({
            //   id: this.max_content_id,
            //   title: _title,
            //   desc: _desc,
            // });

이 방법은 기존에 있었던 contents배열에 값을 하나 추가하는 방식

→ 성능을 개선할 때에 굉장히 까다로움

 

						var _contents = this.state.contents.concat({
              id: this.max_content_id,
              title: _title,
              desc: _desc,
            });
            this.setState({
              contents: _contents,
	            });

이 방법은 기존의 setState가 가지고 있던 값이 새롭게 만들어진 data로 교체하는 방식

→ 성능 개선이 굉장히 쉬움

 

8. shouldComponentUpdate

push : 원본을 바꿈

concat : 원본을 바꾸지 않음

shouldComponentUpdate() : render()함수를 실행할 지의 여부를 결정할 수 있음

 

shouldComponentUpdate(newProps, newState) {
    if (this.props.data === newProps.data) { //이전값과 바뀐값이 같으면 render 실행x
      return false;
    }
    return true;
  }

→ return 값이 false면 render()함수 실행하지 않음!

return 값이 true면 render()함수를 실행함.

→ 새롭게 바뀐 값과 그 이전 값에 접근할 수 있다.

 

9. immutable

→ 원본을 바꾸지 않는다 !

객체를 바꾸고 싶다 : arrayassign !

배열을 바꾸고 싶다 : arrayfrom !

 

섹션6. Update & Delete 기능 구현

1. update 구현

→ read + create기능의 결합

//UpdateContent.js
import React, { Component } from "react";

class UpdateContent extends Component {
  render() {
    return (
      <article>
        <h2>Update</h2>
        <form
          action="/create_process"
          method="post"
          onSubmit={function (e) {
            e.preventDefault();
            this.props.onSubmit(e.target.title.value, e.target.desc.value);
            alert("s");
          }.bind(this)}
        >
          <p>
            <input type="text" name="title" placeholder="title"></input>
          </p>
          <p>
            <textarea name="desc" placeholder="description"></textarea>
          </p>
          <p>
            <input type="submit"></input>
          </p>
        </form>
      </article>
    );
  }
}

export default UpdateContent;

2. form

inputFormHandler 함수를 만들어서 코드를 간결하게 표현

→ [e.target.name] 를 이용해 title, desc에 접근 가능

 

3. state변경

					<UpdateContent
          data={_content}
          onSubmit={function (_id, _title, _desc) {
            var _contents = Array.from(this.state.contents); //this.state.contents를 복사한 새로운 배열이 만들어짐
            var i = 0;
            while (i < _contents.length) {
              if (_contents[i].id === _id) {
                _contents[i] = { id: _id, title: _title, desc: _desc };
                break;
              }
              i = i + 1;
            }
            this.setState({
              contents: _contents,
            });
          }.bind(this)}
	        ></UpdateContent>

4. delete구현

window.confirm → 사용자에게 진짜 삭제할 건지 다시 물어봐줘!

					<Control
          onChangMode={function (_mode) {
            if (_mode === "delete") {
              if (window.confirm("really?")) {
                var _contents = Array.from(this.state.contents);
                var i = 0;
                while (i < this.state.contents.length) {
                  if (_contents[i].id === this.state.selected_content_id) {
                    _contents.splice(i, 1);
                    break;
                  }
                  i = i + 1;
                }
                this.setState({
                  mode: "welcome",
                  contents: _contents,
                });
                alert("deleted!");
              }
            } else {
              this.setState({
                mode: _mode,
              });
            }
            this.setState({
              mode: _mode,
            });
          }.bind(this)}
        ></Control>
반응형