본문 바로가기

[자기계발]/유튜브로 코딩배우기

[생활코딩]React 강의-2 15~16강 State, props, bind 소개

반응형

15.1강 State 소개

유저 인터페이스 : props - props는 사용자가 Component를 사용하는 입장에서 중요한 

내부적 조작 장치 : state - state는 props의 값에 따라서 내부의 구현에 필요한 데이터들 

 

React와 같은 시스템이 그 Component를 만들고 그 Component가 좋은 부품이 되기 위해서는 그 Component를 사용하는 외부의 props라고 하는것과 그 props에 따라서 그 Componet를 실제로 구현하는 내부의 state 라고 하는 정보가 철저하게 구분되어 있어야한다. 양쪽의 편의성을 각자 도모해야한다. 복합적으로 좀 더 다양한 일들을 하는 Componet를 만들 때 필요한 필수적인 요소인 state를 살펴 볼 것이다.

 

전선이 삐져나와있는 핸드폰 느낌

 

15.2강 State 사용

Componet가 실행될 때 constructor라는 함수가 있다면 얘가 먼저 실행돼서 초기화를 담당한다. 스테이트 값을 초기 값으로 초기화 시키는 것.

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 = {
      subject:{title:'WEB2', sub:'World wide web!'}
    }
  }
  render() {
    return (
      <div className="App">
        <Subject title={this.state.subject.title} sub={this.state.subject.sub}></Subject>
        <TOC></TOC>
        <Content title="HTML!" desc="html is HyperText Markup Language."></Content>
      </div>
    );
  }
}

export default App;

 

React에서 우리가 props에다가 " "로 묶어주면 개는 문자열이 되기 때문에

저것이 그대로 나온다. 고로 문자가 아니라 js의 코드로써 실행되게 하고 싶을 때는.  중괄호를 써주면 된다.

 

<Subject title="this.state.subject.title!" sub="world wide web!"></Subject>

""을 { }로 바꿔주었다.

 

상위 컴퍼넌트인 APP의 상태를(class App extends Component )하위 컴퍼넌트로(<Subject title=~) 전달하고 싶을 때는 상위 컴퍼넌트에 state 값을 하위 컴퍼넌트에 props의 값으로 전달하는 것이 가능하다. Subject라고 하는 Component 에 props 값을 준 것이다.

 

외부에서 알 필요가 없는 정보를 철저하게 숨기는 것이 좋은 사용성을 만드는 핵심이다.

전선이 삐져나와있는 핸드폰 느낌. 이런 핸드폰은 아무도 사용하고 싶지 않다.

index.js에서는 우리가 app.js에서 쓴 state를 모른다. 단지 index.js에는 밑에 처럼 나와있을 뿐이다.

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

15.3강 key

subject라는 property에 값 하나를 줬지만 만약 여러개를 쓴다면?

TOC목록을 수정할 때마다 거기에 들어가서 바꾸는 것은 번거로우니, TOC의 부모가 가지고 있는 state를 이용해서 TOC의 내부 데이터가 바뀌게 해보자.

 

contents 라고 하는 property에 state를 추가. 데이터가 여러개 이기 때문에 대괄호를 이용하여 '배열'을 만들자.

참고: 배열은 대괄호, 객체는 중괄호를 쓴다. 

class App extends Component {
  constructor(props){
    super(props);
    this.state = {
      subject:{title:'WEB2', sub:'World wide web!'},
      contents: [
        {id:1, title:'HTML', desc:'Html is for information'},
        {id:2, title:'CSS', desc:'CSS is for desgin'},
        {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>
        <TOC data={this.state.contents}></TOC>
        <Content title="HTML!" desc="html is HyperText Markup Language."></Content>
      </div>
    );
  }
}

#contents에 담겨있는 배열을 TOC에 주입하고 싶다면?

data라는 props로 this.state에 contents를 주입

<TOC data={this.state.contents}></TOC>

 

#while 반복문 사용

props의 데이터의 랭스 만큼 반복한다.

반복할 때마다 i의 값은 i+1이 된다.

밑에 있는 태그들을 lists 라는 배열에 담는다.

li태그가 하나하나 생성돼 lists라고 하는 변수에 담기게 된다.

이 배열 lists를 <ul>사이에 배치해보자.

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><a href={"/content/"+data[i].id}>{data[i].title}</a></li>);
        i= i+1;
    }
      return (
        <nav>
          <ul>
            {lists}
          </ul>
        </nav>
      );
    }
  }

  export default TOC;

엘리먼트 여러개를 자동으로 생성하지 않은 경우에는 에러가 발생한다. 고로, 각각의 리스트에 항목들은 key라고 하는 props를 가지고 있어야 한다 = 각각의 목록을 다른 것들과 구분할 수 있는 식별자를 주면 된다.

우리의 경우 data.[i].id값을 또 적어주면 되는데 우리가 만들고 있는 application에서 사용하는 것이라기 보다는

리액트가 내부적으로 필요해서 다들 요청하는 것으로 그냥 그러려니 하고 넣으면 된다. 그러면 에러가 발생하지 않는다.

        lists.push(<li key={data[i].id}><a href={"/content/"+data[i].id}>{data[i].title}</a></li>);

state와 props의 미묘한 관계를 보았다.

부모의 app입장에서는 state라는 내부정보를 사용했고, 자식한테 전달할 때는 props라는 것을 통해서 전달 했다.

app의 입장에서는 토픽이 내부적으로 어떻게 돌아가는 지 알 필요가 없다. 그냥 데이터라고 하는 props로는 '어떤 형태의 정보를 전달하면 되는가' 하는 사용자의 입장에서 알아야할 것만 알면된다.

 

참고: react는 url을 통한 이동을 지원하지 않아서 router와 같은걸 이용해야 하는데 아직 이부분 구현이 되지 않아서 이동이 안 되고 그냥 app.js single page만 rendering 됩니다

 

Q. "/content/"의 역할은 무엇인가요? 없어도 구동이 되는데 무슨역할을 하는지 궁금해서 질문드려요

A. 지금은 각각의 link를 연결하지 않은 상황이라 임시로 넣어둔 것 같습니다. 이후 html, css, js 각각에 대한 title과 description을 담은 문서를 content라는 폴더에 생성해놓으면 그 파일로 연결되게끔이요.

16.1강 이벤트 state props 그리고 render 함수

링크를 클릭하면 그에 따라 app.js 의 component에 state가 바뀌고

그 바뀐 스테이트가 이 컨텐트 컴퍼넌트의 프로세스  값으로 전달

됨으로써 동적으로 application이 바뀌는 것을 구현 하는 게 우리의 

꿈이고 목표이다.

 

스테이트 세팅하기

 

welcome page인지 read페이지 인지 구별하기 위해서

state에다가 mode 라고 하는 값을 줄 것,.

그리고 기본값에다 welcome

welcome 상태일 때는

 

state에 대해 알아야 할 중요한 사실 

react에서 state 값이 바뀌면, 그 스테이트를 가지고 있는

컴포넌트의 렌더 함수가 다시 호출 된다.

그 랜더 함수가 다시 호출됨에 따라 그 랜더

함수 하위에 있는 컴퍼넌트 들도 각자 랜더 함수가 있는데

개들도 싹다  호출이 된다 = 화면이 다시그려진다

렌더가 하는 일은 어떤 html을 그릴것인가를 결정한다

 

props나 state값이 바뀌면 해당되는 컴퍼넌트의 렌더 함수가 호출되도록

약속되어 있다 props 나 state 가 바뀌면 화면이 다시 그려진다

 

mode의 값에 따라 만들어지느 랜더링 결과가 달라지게 조건문 사용해보자.

 

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:'WEB2', sub:'World wide web!'},
      welcome:{title:'welcome', desc:'Hello, React!!'},
      contents: [
        {id:1, title:'HTML', desc:'Html is for information'},
        {id:2, title:'CSS', desc:'CSS is for desgin'},
        {id:3, title:'JavaScript', desc:'JavaScript is for interactive'}
      ]
     }
  }
  render() {
    console.log('App 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>
        <TOC data={this.state.contents}></TOC>
        <Content title={_title} desc={_desc}></Content>
      </div>
    );
  }
}

export default App;

 

그럼 react에서는 링크를 클릭하면 다른 url을 타고 이동하는 개념이 아니라, event를 발생시켜서 state와 props를 변경함으로써 페이지 전체가 re-rendering 되면서 페이지가 state와 props에 맞게 다시 출력될 뿐인 것인가요?

16.2강 이벤트 설치

우리가 쓰는 것은 유사 html이다. 고로 js 처럼 소문자가 아닌

onClick 처럼 사이에 대문자를 써야한다.

또 중괄호를 써야한다. 

 

이름이 없는 사용자 정의 함수는 링크를 클릭했을 때 실행하도록 약속되어있다,

리엑트의 장점은 역동적인데 이렇게 누르면 새로고침 되니까 자존심이 상한다

 

a라고 하는 태그는 그것을 클릭했을 때 href 속성이 가리키는 페이지로 이동한다라는 기본적인 동작방법을 따른다.

나는 그것을 못하게 하고 싶다

이 함수가 호출될 때 이벤트 라고 하는 객체를 주입해주기로 약속했다

 

    return (
      <div className="App">
        {/* 
        <Subject title={this.state.subject.title} sub={this.state.subject.sub}></Subject>
         */}
         <header>
          <h1><a href="/" onClick={function(e){
            console.log(e);
            debugger;
          }}>{this.state.subject.title}</a></h1>
          {this.state.subject.sub}
        </header>
         <TOC data={this.state.contents}></TOC>
        <Content title={_title} desc={_desc}></Content>
      </div>
    return (
      <div className="App">
        {/* 
        <Subject title={this.state.subject.title} sub={this.state.subject.sub}></Subject>
         */}
         <header>
          <h1><a href="/" onClick={function(e){
            console.log(e);
            e.preventDefault();
          }}>{this.state.subject.title}</a></h1>
          {this.state.subject.sub}
        </header>
         <TOC data={this.state.contents}></TOC>
        <Content title={_title} desc={_desc}></Content>
      </div>

 

16.3강 이벤트에서 state 변경하기

    return (
      <div className="App">
        {/* 
        <Subject title={this.state.subject.title} sub={this.state.subject.sub}></Subject>
         */}
         <header>
          <h1><a href="/" onClick={function(e){
            console.log(e);
            e.preventDefault();
            // this.state.mode = 'welcome';
            this.setState({
              mode: 'welcome'
            });
          }.bind(this)}>{this.state.subject.title}</a></h1>
          {this.state.subject.sub}
        </header>
         <TOC data={this.state.contents}></TOC>
        <Content title={_title} desc={_desc}></Content>
      </div>
    );
  }
}

this.state.mode = 'welcome';

을 했는데 실행이 안 되었다. 리엑트는 state 값이 바뀐 것을 모르기 때문이다.

이 함수 안에서는 this 값이 컴퍼넌트 자기 자신을 가리키지 않고 아무 값도 세팅되어 있지 않다

그래서 우리가 읽으려고 했던 state를 알 수 없다 하는 것

            this.setState({

              mode: 'welcome'

            });

          }.bind(this)}>{this.state.subject.title}</a></h1>

 

이벤트를 설치 할 때, this를 찾을 수 없다면 -> 함수가 끝난 직후에  .bind(this)를 추가해주면 된다. 이러면

함수안에서 this는 우리의 컴포넌트가 된다.

그런데 안바뀐다. 왜? 다시. 리엑트는 state 값이 바뀐 것을 모르기 때문이다.

 

고로 react 사용설명서에 있는대로 해야 한다.

this.setState({

mode: 'welcome'

});

setState 라는 함수를 추출할 때, 인자를 우리가 바꾸고 싶은 값인 mode. 'mode'를 welcome으로 바꾸고 싶다.

render 함수도 이에 따라 Hello, React!! 로 바뀌었다.

 

2가지 조치

  • bind
  • setState

왜 이렇게 해야되나 몰라도 react 할 수 있다. 다만, 알면 자유도가 높아지고 기억해야 될 것도 줄어든다

 

 

localhost:3000에서 WEB을 누르면 밑의 HTML이 

 

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

 

bind = 뭔가를 엮는다. 묶어준다

기본적으로 render라고 하는 함수가 호출 될 때, 이 함수 안에서 콘솔 로그 하기

  render() {
    console.log('App 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;
    }
    console.log('render', this);
    return (
      <div className="App">
        {/* 
        <Subject title={this.state.subject.title} sub={this.state.subject.sub}></Subject>
         */}
         <header>
          <h1><a href="/" onClick={function(e){
            console.log(e);
            e.preventDefault();
            // this.state.mode = 'welcome';
            this.setState({
              mode: 'welcome'
            });
          }.bind(this)}>{this.state.subject.title}</a></h1>
          {this.state.subject.sub}
        </header>
         <TOC data={this.state.contents}></TOC>
        <Content title={_title} desc={_desc}></Content>
      </div>
    );
  }

render안에서 this는 이 render 함수가 혹해 있는 그 컴퍼넌트 자체를 가리킨다.

onClick 함수는 이상하게 this가 아무 값도 없다.

onClick={function(e){
            console.log(e);
            e.preventDefault();
            // this.state.mode = 'welcome';
            this.setState({
              mode: 'welcome'
            });
          }

 

>var obj = {name: 'egoing'};

=> undefined

 

>function bindTest(){

 console.log(this.name);

}

=> undefined

 

>bindTest();

=> undefined

this가 될 이유가 없어서 값이 안나온다.

 

bindTest.bind(obj);

bindTest함수 bind라는 함수를 또 준다. 그리고 이 바인드라는 함수 첫번 쨰 인자로 obj를 준다.

그러면 bindTest라는 함수 안에서 this는 obj가 된다. bindTest 2라는  새로운 함수가 만들어진다. 

>var bindTest 2 = bindTest.bind(obj);

=> undefined

 

>bindTest2(); 

=> egoing

bindTest2라는 함수는  bindTest.bind(obj); 코드로 인해서 이 함수 내부console.log(this.name);에서

this의 값이 obj가 됐기 때문이다.

 

 

onClick에서 bind(this)를 하게 되면 app이라고 하는 컴퍼넌트 자체를 가리키는 객체를

밑의 함수 안으로 주입해서 함수 안에서 this는 그 객체가 되게 하는 것이다.

onClick={function(e){
            console.log(e);
            e.preventDefault();
            // this.state.mode = 'welcome';
            this.setState({
              mode: 'welcome'
            });
          }

이것이 바로 bind 라고 하는 함수가 하는 일이다.

 

 

#제가 bind와 this에 관해서 정리한 글이 있습니다! 왜 화살표 함수를 사용하면 bind가 없이 this를 사용할 수 있는지와  this에 대한 정확한 개념, this우회 방법등등 https://wonit.tistory.com/242?category=762174 최대한 쉽게 설명을 한다고 했으니 와서 가볍게 읽고 이해하시는 것도 방법일것 같네요 ㅎㅎ :)

 

#this바인딩은 함수 호출 방식에 따라 다르게 결정됩니다. 이벤트 핸들러 어트리뷰트의 값으로 지정한 함수는 이벤트핸들러에 의해 일반함수로 호출되고 this는 전역객체인 window를 가리키게 됩니다. 다만 'strict mode'가 적용된 일반 함수 내부의 this에는 window객체가 아닌 undefined가 바인딩 되는데, class의 내부는 암묵적으로 strict mode가 적용되기 때문에 this는 undefined가 되는 것 입니다. 그러나 bind메서드를 사용해서 명시적으로 this를 바인딩 해줌으로써 this가 App Class를 가리키도록 해주는 것입니다.

 

#render() 메서드 밖에서 ES6의 화살표 함수로 이벤트 함수를 만들어 넘겨주면 .bind 없이 this를 사용할 수 있습니다. handleClick = (e) => { console.log(e); e.preventDefault(); this.setState({ mode: 'welcome' }); } render() { ... return( <h1><a href="/" onClick={this.handleClick}>{this.state.subject.title}</a></h1> {this.state.subject.sub} ); } 참고: https://blueshw.github.io/2017/07/01/arrow-function/

 

[ES6, react] 리액트에서 화살표 함수(arrow function)는 선택이 아닌 필수

리액트를 개발하다보면 이런 코드를 본적 있을것입니다. this(아마도 react 클래스 객체)에 속한 어떤 메서드를 다시 this 에 bind 한다라?? 굳이 왜 이런짓을 해야하는지 의문이 들만합니다. 리액트

blueshw.github.io

 

#JS에서 this는 실행 컨텍스트가 생성될 때 결정됩니다. 따라서, 함수 호출시 this가 결정된다고 볼 수 있습니다. JS의 모든 변수는 실은 특정 객체의 프로퍼티로서 동작하기 때문에, var로 변수를 선언해도 실제 JS 엔진은 어떤 특정 객체 (LexicalEnvironment)의 프로퍼티로 인식합니다. 위의 경우처럼 메소드가 아닌 함수로써 호출시에는 this(nodejs는 global)이 할당되고 .함수명 과 같이 메소드로써 호출하면 this는 호출 주체인 그 메소드를 가리킵니다. ※ 요약: 함수로서 호출하는 것은 호출 주체 (객체)를 명시하지 않고, 코드에 개발자가 관여해서 실행한 것이기 때문에 호출 주체 정보를 알 수 없고, 실행 컨텍스트에서 활성화 당시 this가 지정되지 않아 전역 객체(window)를 바라본다. 여기서는 윗 댓글에서 말한 class 내에서의 use strict로 인해 undefined 이다. ※※갸꿀팁: 호출될 때 객체명.메소드() 로 호출되면 메소드임. 이 때는 this가 객체를 가리키고, 함수() 는 전역 혹은 undefined

 

#function 내부 this에 관해서 왜 그럴까 생각하다가 그냥 든 생각입니다. function안에 함수자체는 아마 텍스트 자체로 렌더링 됐을것 같습니다. 소스만 봤을때는 class내부안에 있어서 this가 app을 볼 것 같지만 실제 function안 this 텍스트 그대로 html로 바뀌어서 실제 클릭해서 함수가 실행 될때 this 입장에서는 가리키는게 없는 undefined인거죠 .bind(this) 라는 함수를 뒤에 붙여 줌으로써 저안에 저 함수가 실행 될때 app클래스 하위에 있다라고 정해줘서 function함수가 실행될때의 this는 app클래스를 가르킬수 있지 않을까 싶습니다.

 16.5강 이벤트 setState 함수 이해하기

 

이 챕터에서는 state 값을 직접 변경하면 안되고 함수의 형태로 변경해야 되는 이유에 대해서 설명한다.

 

이미 컴퍼넌트가 생성이 끝난 다음에는

            this.setState({

              mode: 'welcome'

            });

를 통해 변경하고 싶은 값을

 

객체 형태로 줘야한다. 이렇게 짜면 저 함수가 호출되면서 그 함수가 내부적으로 많은 일을 하는데 

그 일을 할 수 있도록 하는 것이 setState 인것! 고로, 항상 state 값이 바뀌면  setState 로 바꿔 주어야 한다

 

constructor 에서는 밑에 처럼 해도 괜찮다

class App extends Component {
  constructor(props){
    super(props);
    this.state = {
      mode:'read',
      subject:{title:'WEB', sub:'World Wid Web!'},
      welcome:{title:'Welcome', 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'}
      ]

 

 

 this.state.mode = 'welcome';

이렇게 바꿔버리면 react 입장에서는 몰래 바꾸는 셈이다. 즉 값이 바뀐 지 모르니까 render를 할 수 없다

 

안바뀌면 render가 호출이 안된 것

 

 

 

 

Q.

근데 왜 계속 이해가 안되도 계속 나아가라고 하시나요? 이해가 안되면 멈추고 더 공부한후에 진행되야 되는거 아닌가요?? 참고로 전 다 이해가 된다만 기초가 부족한분들은 무작정 따라한다고 얻는게 없을것같은데..

 

A.

기초가 부족하기 때문에 이해가 어려운 부분은 그냥 따라하는 게 더 빠르고 쉬워요. 개발할 때 모든걸 다 이해하고 한다기 보단, 그냥 원래 남들이 쓰는건 복사해서 붙여넣어 쓰는게 더 빨라요. 이해해서 나쁠 건 없지만 개발할 때 반드시 이해가 필요하지 않은 함수의 기능을 해부하다시피 하면 개발 못해요ㅠㅠㅠㅠ 일일히 모든 함수를 파헤치기엔 함수의 종류도 많고 내부적인 로직이며 양이 정말 많고 복잡하기 때문에 다 이해할 수 없으니까요 이해 안해도 되는 부분을 콕 찝어주시기 때문에 이 강의가 좋은거예요. 불필요한 노력을 최소화할 수 있어서요. 나중에 개발하다가 특정 함수의 로직이 궁금하다 싶으면 그때 까보시는게 좋아요! 여기서는 state값을 변경 할 때 setState()를 써야하는 이유. state값 직접 변경은 constructor 내부에서만 가능하다. constructor밖에서는 직접 변경이 불가능하고 setState()를 통해 변경해줘야한다. 정도만 확실히 기억하고 나중에 state값을 변경할 때 사용하면 될 것 같아요.

반응형