오늘도 난 존버한다

React 생활코딩 - 15. 컴포넌트 이벤트 만들기 본문

React

React 생활코딩 - 15. 컴포넌트 이벤트 만들기

lunina 2024. 5. 18. 20:44

이번엔 WEB의 mode를 read로 바꾸고 

클릭한 contents가 아래 본문에 나오게 해보자

 

먼저 contents를 부르는 TOC에 onChangePage 함수를 넣어주자

App.js

 

TOC.js 파일로 들어가서 HTML, CSS, JavaScript 각각의 목록에 해당되는 부분을 찾아준다

 

이를 줄바꿈을 해주고 onClick함수를 넣어준다

이때 e.preventDefault를 넣어주고 + bind(this), 

 

 

App.js의 TOC에도 TOC.js 함수를 받을 수 있게 처리해준다.

이 때 alert를 넣어 실행되는지도 확인할 수 있다

 

contents를 클릭해 alert가 실행된다

 

이제 alert를 지우고 그 자리에 this.setState({mode: 'read'});를 입력해준다.

 

이제 WEB을 클릭하면 'welcome'으로 contents를 클릭하면 HTML 로 변경된다!

이제 각 contents를 클릭했을 때 화면에 표시될 해당 값을 넣어보자. 

 

먼저 state에 기본 content를 설정하자.

기본은 2를 준다selected_content_id:2,

 

그리고 contents 중 어떤 것이 본문에 나올 것인가? 를 지정해줘야 한다

 

else if 의 _title과 _desc 의 값을 변경해 줘야한다.

이 때 반복문을 사용할 수 있다.

var i = 0;

while( i < this.state.contents.length){

 i = i+ 1;

}

 

여기서 강제로 while 문을 끝내버리는 break문을 사용하면

break 가 실행되는 순간 조건문이 끝나고 조건문 밖에있는 코드들이 실행되기 시작한다

 

실행

콘솔 창에서 Component를 직접 바꿔주면 아래 본문의 내용도 같이 바뀌는 것을 확인할 수 있다.

 

이제 TOC 리스트에 onChangePage 이벤트가 발생했을 때, this.setState를 통해서 mode의 값과 함께 selected_content_id:0 의 값을 0이라고 되어있는 부분에 해주면 된다. 어떻게 해야할까?

 

그것은 App.js의 <TOC> onChangePage 이벤트를 실행시키는 부분인 TOC.js에서 onClick을 했을 때 바로 this.props.onChangePage(); 실행시키는 것을 통해서  App.js의 <TOC> onChangePage 함수를 실행해준다

그렇다면 contents 중 클릭한 항목의 id값을 주면 되지 않을까?

 

방법은 여러가지가 있지만, 생활코딩에서는 기본적인 것으로 진행했다.

 

TOC.js 에 방법을 적어보자

data-id={data[i].id 속성을 준다.

그리고 실행하면 콘솔에서 아래처럼 확인할 수 있다.

 

그렇다면 onClick 이벤트가 발생했을 때 저 e라고 하는 이벤트 객체는 어떤 정보가 있는지 한번보자

debugger을 넣어준다

 

다시 콘솔을 키고 실행해주면 아래처럼 화면이 나온다

ESC  를 눌러 console창을 열어주고 e 를 입력해준다

e에 대한 많은 속성들을 확인할 수 있다

이 중에 중요한 속성이 있다.

이벤트 객체는 target이라는 속성이 있고 target은 그 이벤트가 발생한 태그 즉 여기에서는 a태그를 가르킨다

이벤트 함수 내에서 e.target이라고 하면 저 이벤트가 소재하고 있는 태그인 이 a 태그를 가르킨다 이것을 통해 a태그가 갖고있는 data 다시 id 값을 알아낼 수 있다.

 

위의 source에서 a 태그를 클릭하면 오른쪽에서 target은 a태그라고 확인할 수 있다.

그리고 data- 인 접두사로 시작되는 속성은 dataset이라고하는 특수한 것을 통해 접급할 수 있다.

dataset을 클릭하면 id:2 라고 나오는데 이 값이 바로 {data[i].id} 값이다!

 

e.target.dataset.id라고 console에 입력하면? 값은 2로 나온다

 

이렇게 알아낸 정보를 TOC.js의 onChangePage 함수로 호출하는 코드에 인자로 넣어준다

 

그리고 App.js의  onChangePage에서 id라는 매개변수를 준고 debugger를 또 찍어본다

 

다시 실행해보면 id 값이 잘 넘어오는 것을 확인 할 수 있다.

 

그렇다면 debugger을 지우고 저 id 값을 그대로 selected_content_id에 0이 아닌 id를주면 된다

 

다시 실행해주면 클릭된 id 값을 잘 받아온다

그런데 본문 글자가 화면에 보이지 않는다.

이유는 함수에 들어온 id의 값은 문자인데 contents의 id는 숫자다.

즉 데이터 형식이 다르다

 

그래서 문자를 숫자로 강제변환을 시켜준다 

자바스크립트 명령 중 하나인 Number을 넣어준다 

정리

여기 onClick이라고하는 저 속성을 이벤트를 실행시킬 때 e.target.dataset.id를 통해 값을 추출했는데  

만약 data-id가 아니라 data-abs다 그러면 아래도 똑같이 바꿔주면된다

 

속성을 이용하지 않고 하는 방법 

bind의 두번째 인자로 data[i].id 값을 주면 bind는 function 함수에 두번 째 인자로 들어온 이 값을 이 함수의 첫번째 매개변수 의 값으로 넣어준다. 그리고 기존의 값은 한 칸씩 뒤로간다.

그래서 아래처럼 바꿔주어도 똑같이 동작할 수 있다.

 

< App.js 전체코드 >

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: 'read',
      selected_content_id:2,
      subject:{title:'WEB', 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 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'){
      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 += 1;
      }
    }
    return (
      <div className="App">
        <Subject
          title={this.state.subject.title}
          sub={this.state.subject.sub}
          onChangePage={function(){
            this.setState({mode:'welcome'});
          }.bind(this)}
        >
        </Subject>
        <TOC
          onChangePage={function(id){
           this.setState({
              mode:'read',
              selected_content_id:Number(id)
            });
          }.bind(this)}
          data={this.state.contents}>
        </TOC>
        <Content title={_title} desc={_desc}></Content>
      </div>
    );
  }
}

export default App;

 

< TOC.js 전체코드 >

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={"/content/"+data[i].id}
                data-id={data[i].id}
                onClick={function(e){
                  e.preventDefault();
                  this.props.onChangePage(e.target.dataset.id);
                }.bind(this)}
                >{data[i].title}</a>
            </li>);
            i = i + 1;
        }
      return (
        <nav>
          <ul>
              {lists}
          </ul>
        </nav>
      );
    }
  }

  export default TOC;

 

< Subject.js 전체코드 >

import React, { Component } from 'react';

class Subject extends Component {
    render(){
      return (
        <header>
          <h1><a href="/" onClick={function(e){
            e.preventDefault();
            this.props.onChangePage();
          }.bind(this)}>{this.props.title}</a></h1>
          {this.props.sub}
        </header>
      );
    }
}

export default Subject;

 

< Content.js 전체코드 >

import React, { Component } from 'react';

class Content extends Component {
    render(){
      return (
        <article>
              <h2>{this.props.title}</h2>
              {this.props.desc}
          </article>
      )
    }
  }

  export default Content;