본문 바로가기
JAVASCRIPT/React

[생활코딩 React 강의 5] 이벤트 설치해보기 응용

by sjs_2215 2019. 5. 19.

생활 코딩 리액트 강의 <https://www.youtube.com/playlist?list=PLuHgQVnccGMCRv6f8H9K5Xwsdyg4sFSdi>


나만의 태그에 이벤트를 만들어서 태그/컴포넌트를 생산해보기

(리액트 스터디 4일 차의(리액트 스터디 4일 차의 응용)

하려는 것1:

App.js의 subject 컴포넌트에 onChagePage라는 이벤트를 내가 만듦.

이 이벤트에 alert창 뜨게 하는 함수를 설치

그 이벤트가 발생되었을 때, props로 전달된 onChangePage 함수를 호출하면 됨.

여기까지 해서, 리로딩 방지하고 alert창 잘 뜨면 내가 만든 이벤트에 setState 이용해서 state 바꾸는 함수로 수정

전:

App.js- header 태그 안의 코드는 원래 Subject.js에 있던 코드. 근데 쉽게 이해하기 위해 Subject의 코드를 바깥으로(app.js) 옮겨놨었을 뿐

후:

header 태그 주석 처리하고, 이전에 작업했던 subject 컴포넌트를 살림. 이제 이 컴포넌트에 이벤트를 설치할 것임. "사용자들아 내가 만든

이벤트를 쓰고 싶으면 onChangePage 써

"

즉, subject 컴포넌트는 onChangePage라고 하는 이벤트가 있어서, 이 컴포넌트 안에서 링크를 클릭했을 때 이벤트에 설치한 함수를 호출하도록 만듦. (사용자가 함수를 설치하는 거임)

onChangePage는 props형태로 subject에 전달될 것임.

->Subject.js에서 onChangePage를 호출할 때 this.props.onChangePage();라고 해주면 됨


하려는 것2:

mode를 read로 바꾸고, 목록들을 클릭하면 밑에 해당 description 출력하기

html, css, javascript 버튼 클릭하면 mode를 read로 바꾸는 것을 쉽게 됨.

Subject.js 바꾼 거 참고해서 코드 수정하면 됨

toc.js에서는 a href 태그에서 onclick에 함수 function(e) 만들어서 거기에 preventDefault랑 props로 내가 만든 함수 호출하게 바꿔주고

app.js 에서는 onChangeDesc로 내가 이벤트 만들어주고 mode 바꾸는 setState 코드 넣어주면 됨.

문제점: 각 버튼마다 다른 description이 나와야 되는데 다 똑같은 내용만 출력됨

해결책 1:

(App.js에서)

  • selected_content_id라는 state값을 새로 준다
  • mode가 read일 때 부분-> while문 돌려서 selected_content_id랑 data .id랑 같을지 비교해서 _title, _desc 값 집어넣게 바꾸기
  • TOC 컴포넌트 부분에 onChangeDesc 이벤트 함수가 실행될 때 mode도 바뀌고, selected_content_id도 바꾸게
  • 근데 selected_content_id는 TOC.js에서 인자로 보내주는 값으로 지정할 것

(TOC.js에서)

  • 클릭한 id를 얻기 위해 새로운 속성 data-id를 추가
  • onChangeDesc 호출할 때 앞에서 추가한 속성을 이용해서 e.target.dataset.id를 인자로 넘김.

target은 a태그를 가리킴. data-라는 접두사로 시작되는 속성은 dataset이라고 하는 특수한 것을 통해 접근할 수 있음. 그리고 접두사 뒤에 붙어 있는 id는 data[i].id가 가지고 있는 값임.

이렇게 얻은 id를 app.js에서 내가 정의한 이벤트 함수의 인자로 보내주게 함. 근데 인자로 보내는 id가 char라 app.js에서 int형으로 바꾸어 주어야 함

해당 코드

App.js

import React, { Component} from 'react';
import TOC from "./components/TOC"; //TOC를 App.js에서 쓰기위한 import문
import Subject from "./components/Subject";
import Content from "./components/Content";
import './App.css';

class App extends Component {
  constructor(props){
    super(props);
    this.state = {
      mode:'read',//welcome 페이지, read 페이지 구분 //밑에 if문에서 mode검사할 때 welcome 철자/대소문자 오타 조심
      selected_content_id:2, // 선택된 content를 출력하기 위한 변
      subject:{title:'web', sub:'world wide web'},
      welcome:{title:'Welcome', desc:'Hello, React'}, //welcome mode일 때 content에 표시될 텍스트 지정
      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() {
    console.log('App render'); //render 호출되는지 로그 찍어보기
    var _title, _desc = null;
    if(this.state.mode === 'welcome') { //=== 3개인 거 오타 조심
      _title = this.state.welcome.title;
      _desc = this.state.welcome.desc;
    } else if(this.state.mode === 'read') {
      var i = 0;
      while(i<this.state.contents.length) {
        var data = this.state.contents[i];
        if(data.id === this.state.selected_content_id) {
          _title = data.title;
          _desc = data.desc;
          break;
        }
        i=i+1;
      }

    }
    return (
      <div className="App">
      <Subject
      title={this.state.subject.title}
      sub={this.state.subject.sub}
      onChangePage={function(){
        {/*alert('hihihi');*/}
        this.setState({
          mode:'welcome'
        })
      }.bind(this)}
      >
      </Subject>
      <TOC
      data={this.state.contents}
      onChangeDesc={function(id) {
        this.setState({
          mode:'read',
          selected_content_id:Number(id)
        })
      }.bind(this)}
      >
      </TOC>
      <Content title={_title} desc={_desc}></Content>
      </div>
    );
  }
}

export default App;

TOC.js

import React, { Component} from 'react';
//리액트라는 라이브러리에서 컴포넌트라는 클래스를 로딩한 것.


class TOC extends Component {
  render(){
  console.log('TOC render'); //render 호출되는지 로그 찍어보기
  var lists=[];
  var data=this.props.data;
  var i=0;
  while(i<data.length){
      lists.push(
        <li key={data[i].id}>
        <a
        href={"/content/"+data[i].id}
        data-id={data[i].id} //클릭한 id를 얻기 위해 새로운 속성 추가
        onClick={function(e){
          e.preventDefault();
          this.props.onChangeDesc(e.target.dataset.id);
        }.bind(this)}>
        {data[i].title}</a>
        </li>);
      i=i+1;
  }
    return(
      <nav>
        <ul>
          {lists}
          </ul>
        </nav>
    );
  }
}
export default TOC;
//이 export문을 써줌으로써 TOC라는 클래스를 다른 파일이 가져다 쓸 수 있음.

  • 상위 컴포넌트(app.js)가 하위 컴포넌트(subject, content, toc.js)로 값을 전달할 때 props를 통해 전달
  • 하위 컴포넌트가 상위 컴포넌트의 값을 바꾸고 싶을 때 이벤트를 통해서 함

ex) 버튼 클릭했을 때 mode와 selected_content_id를 바꾸고 싶은 경우 (toc가 app의 무언가를 바꾸고 싶어 하는 경우)

->이벤트를 구현해서, 이벤트가 트리거 됐을 때 상위 컴포넌트의 state 값을 호출하는 것을 통해서 state 값을 바꿀 수 있음

App.js의 일부분

<TOC
      data={this.state.contents}
      onChangeDesc={function(id) {
        this.setState({
          mode:'read',
          selected_content_id:Number(id)
        })
      }.bind(this)}
      >
      </TOC>
  • Redux

위처럼 데이터를 분산해서 보관하는 것이 아닌 하나의 저장소에 저장. 저장소의 값이 변경되면 이와 관련된 모든 컴포넌트의 값도 변경되게 하는 테크닉

Comments