React(생활코딩)_6일차_컴포넌트 이벤트 만들기_1

Lina Hongbi Ko·2023년 8월 23일
0

React_생활코딩

목록 보기
7/23

이전시간에는 이벤트, state값을 바꿔서 props를 변경하는 방법, render함수에 대해서 배웠다면 오늘은 컴포넌트에 직접 이벤트를 만드는 것을 실습한다.

컴포넌트 이벤트 만들기

: 지금까지 이벤트를 계속 사용하기만 했다. 하지만 이제 태그의 이벤트를 직접 만들어서 해당 태그 또는 컴포넌트를 사용하는 방법을 배워보려 한다. (이벤트의 소비자 -> 이벤트의 생산자가 되어보자.)

일단, 이전 시간에 작성했던 코드를 보면

// App.js 파일

...생략...

render() {
... 생략 ...
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.setState({mode:"welcome"});
                }.bind(this)}>{this.state.subject.title}</a>
            </h1>
            {this.state.subject.sub}
        </header>
        <TOC title={this.state.contents}></TOC>
        <Content title={_title} desc={_desc}></Content>
	</div>
... 생략 ...

header태그 안의 a태그를 클릭하면 onClick 이벤트에 설정해둔 사용자정의함수가 호출되면서 App 컴포넌트의 mode값이 "welcome"으로 변하는 것을 볼 수 있다.
하지만 원래 우리가 주석처리 한 header 윗 부분의 코드처럼 작성해서 단순하고 정리되게 만들어야 한다.

// App.js 파일

...생략...

render() {

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

기존 header부분을 주석처리하고 그 자리에 Subject 컴포넌트를 다시 넣었다. 이제 이 Subject 컴포넌트를 가지고 우리가 하려는 것은 아래와 같다.

// App.js 파일
... 생략 ...
class App extends Component {
	... 생략 ...
    return(
    	<div className="App">
        	<Subject title={this.state.subject.title} sub={this.state.subject.sub} onChangePage={function(){
            	alert("hihihi");
            }.bind(this)}></Subject>
    ... 생략 ...

상위컴포넌트 App 컴포넌트에서 props의 하나로써 onChangePage라는 이벤트를 속성으로 지정한다. Subject 컴포넌트는 onChangePage 이벤트를 props로 받아, 컴포넌트 안의 a링크를 클릭했을때 사용자가 설치한 함수를 호출하게 만든다. (이 Subject 컴포넌트는 페이지가 바뀌었을때, 즉 누군가 a링크를 클릭했을때 사용자가 설치한 함수를 호출한다.)

HOW?

: onChangePage이벤트를 Subject 컴포넌트에 props로 전달한다.

Subject 컴포넌트를 고쳐보자.

// 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)}>
                </h1>
            </header>
        );
    }
}

App.js 파일의 주석처리 부분을 Subject.js 파일에 옮겨 적어주었다.

자세히 코드를 설명하면, 우리는 onChangePage 라고 하는 이벤트를 만들었고, 이벤트에 사용자가 함수를 설치해 이벤트의 내용을 적는다. 그리고 해당 이벤트가 발생했을때, 즉 Subject 컴포넌트의 a링크를 클릭했을때 props로 전달된 onChangePage이벤트 함수가 호출된다.

💡 정리 :
App.js파일에서 Subject 컴포넌트에 직접 onChangePage 이벤트를 만듦 -> 이 이벤트는 사용자정의 함수를 가지고 있어서 함수 안에 원하는 이벤트가 발생하도록 만들 수 있음(bind함수 적어야함) -> Subject.js 파일에서 onChangePage 이벤트를 props로 전달받아서 실행할 수 있게 함 & 함수를 호출시킴(this.props.onChangePage()) -> 이벤트발생

자, 이제 원리를 알았으니 state값을 변경해보자.

원하는 이벤트가 state의 mode 값을 변경하는 것이므로 Subject 컴포넌트의 props인 onChange의 사용자 함수를 수정해보자. state을 수정하는 것은 setState함수와 bind함수를 이용해야 하므로 아래처럼 작성해준다.

// App.js 파일
... 생략 ...
class App extends Component {
	... 생략 ...
    return(
    	<div className="App">
        	<Subject title={this.state.subject.title} sub={this.state.subject.sub} onChange={function(){
            	this.setState({mode:"welcome"});
            }.bind(this)}></Subject>
    ... 생략 ...

이렇게 고치고나서 WEB을 클릭하면 다시 아래의 제목과 텍스트가 바뀐것을 확인할 수 있다. 이전시간에는 소비자의 입장에서 단지 onClick이벤트를 바로 작성해서 사용했지만, 지금은 생산자로서 내가 직접 이벤트를 만들어서 사용한 것이다.

!!

생코님의 말대로 이벤트의 소비자에서 이벤트의 생산자가 되었다:)

주석부분을 삭제하고 다시 자세히 살펴보면,

// App.js 파일
... 생략 ...

class App extends Component {
	... 생략 ...
    return(
    	<div className="App">
        	<Subject title={this.state.subject.title} sub= {this.state.subject.sub} onChange = {function(){
            	this.setState({mode:'welcome'});
            }.bind(this)}></Subject>
            <TOC data={this.state.contents}></TOC>
            <Content title={_title} desc={_desc}></Content>
        </div>
    );
... 생략 ...

이 코드를 통해 우리는 최종적으로 이벤트를 만들고 사용했음을 확인할 수 있다!

2. 각 링크로 이동하게 만들기

: 이전시간부터 '1.아래의 텍스트 바꾸기' 를 통해 이벤트가 무엇인지 그리고 이벤트를 사용해 state값을 바꾸고 props에 넘겨주면서 상태를 바꾸고, 또 위에서는 직접 이벤트를 만들어서 state값을 바꾸었다.

이제 '2. 각 링크로 이동하게 만들기'를 통해 앞에서 배운 것들을 이용해 코드를 작성할 것이다.

요번에는 결과 화면의 목록 부분을 클릭했을때 App 컴포넌트의 state에 있는 mode를 "read"로 바꾸고 클릭한 것에 해당하는 콘텐츠가 나오게 만들어볼 것이다.

먼저 TOC 컴포넌트에 onChangePage 이벤트를 만들고, 이벤트에 원하는 함수를 지정해 사용할 예정이다.
일단, 경고창이 화면에 나오도록 props에 추가해보자.

// App.js 파일

... 생략 ...

class App extends Component {
	... 생략 ...
    return(
    	<div className="App">
        	... 생략 ..
            <TOC data={this.state.contents} onChangePage={function(){
            	alert("hi");
            }.bind(this)}></TOC>
			... 생략 ...

App 컴포넌트에서 TOC 컴포넌트의 props인 onChangePage 이벤트에 경고창을 띄우는 함수를 전달 했으니, TOC 컴포넌트에서는 onChangePage라는 props를 받아야한다. 아래처럼 작성하면,

// TOC.js 파일

import React, { Component } from 'react';

class TOC extends Component {
	render(){
    	... 생략 ...
        for(let i = 0; i < data.length; i++) {
        	lists.push(<li key={data[i].id}><a href={"/content/" + data[i].id} onClick = {function(e){
            	e.preventDefault();
                this.prps.onChangePage();
            }.bind(this)}>{data[i].title}</a></li>);
        ... 생략 ...

리스트의 a링크를 클릭하면 onClick 이벤트가 발생해 사용자 정의 함수가 실행된다. 그리고 새로고침하지 않도록 preventDefault함수가 실행되고 this.props.onChangePage()를 실행해 함수를 호출한다. 그럼 목록을 클릭했을때 경고창이 나오는 것을 확인할 수 있다.
얘를 이제 처음 우리가 하려고 했던 (mode를 read로 변경) 코드로 변경하면,

// App.js 파일

... 생략 ...

class App extends Component {
	... 생략 ...
    return(
    	<div className="App">
        	... 생략 ..
            <TOC data={this.state.contents} onChangePage={function(){
            	this.setState({mode:'read'});
            }.bind(this)}></TOC>
			... 생략 ...

요로코롬 경고창을 보여주는 함수대신 mode값을 'read'로 바꾸도록 코드를 변경해주었다. 이렇게 하면 WEB링크와 list의 목록링크들을 번갈아서 클릭해봤을때 state의 mode값이 변경되는 것을 알 수 있다. (Component탭에서 확인할 것)

자, 이제 그럼 mode를 read와 welcome으로 바꾸는 것까지 만들었으면 목록의 각 링크를 클릭했을때 아래에 해당하는 콘텐츠가 나오도록 바꿔보자.

// App.js 파일
... 생략 ...
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!'},
            ... 생략 ...

App 컴포넌트의 state에 selected_content_id값을 지정해줘서 현재 활성화된 선택된 콘텐츠가 표시되게 만들것이다. 기본적으로 contents 리스트의 2번째 내용을 선택되도록 한다.

그리고 이 값을 바탕으로 어떤 내용이 본문에 나올 것인지 지정할텐데, mode가 'read'일때 콘텐츠의 내용이 바뀌도록 조건문의 코드를 수정한다.

// App.js 파일
... 생략 ...
class App extends Component {
	... 생략 ...
    render() {
    	let _title, _desc = null;
        if(this.state.mode === 'welcome') {
        	_title = this.state.welcome.title;
            _desc = this.state.welcome.desc;
        } else if (this.state.mode === 'read') {
        	for(let i = 0; i < this.state.contents.length; i++) {
            	const data = this.state.contents[i];
                if(data.id === this.state.selected_content_id) {
                	_title = data.title;
                    _desc = data.desc;
                    break;
                }
            }
        }
    }
}
console.log('render',this);
... 생략 ...

요로코롬 위처럼 작성하면 현재 selected_content_id 값이 2이기 때문에 출력되는 콘텐츠의 내용은 this.contents의 두번째 항목인 CSS가 될 것이다.

이제 selected_content_id 값을 변경해보자.
TOC 컴포넌트의 onChangePage 이벤트가 발생했을때 this.setState를 통해 selected_content_id값을 변경하면 된다.

onChangePage함수는 TOC컴포넌트의 a링크를 클릭할때 호출되므로, onChangePage함수를 호출할때 클릭한 항목의 id값을 인자로 전달해주면 된다.

책에서는 두가지 방법을 설명했다.

첫번째, 클릭한 항목의 id값을 인자로 전달해서 출력하게 한다.

// TOC.js 파일
... 생략 ...
class TOC extends Component {
	render() {
    	let lists = [];
        const data = this.props.data;
        for(let i = 0; i < data.length; i++) {
        	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.tartget.dataset.id);
            }.bind(this)}>{data[i].title}</a></li>);
        }
        return(
        	<nav>
            	<ul>{lists}</ul>
            </nav>
        );
    }
}

export default TOC;

a태그에 data-id 속성을 추가하고 id값을 할당했다. 그리고 onClick이벤트 함수의 인자로 전달된 e객체를 사용해서 id값을 구한후, onChangePage 함수를 호출한다.

a링크를 디버거를 지정해서 살펴보면,

<a href={"/content/" + data[i].id} data-id={data[i].id} onClick = {function(e) {
	debugger;
    e.preventDefault();
    this.props.onChangePage(e.target.dataset.id);
}.bind(this)}>{data[i].title}</a>

debugger; 문장이 있는 부분에서 멈춘상태에서 Console탭에 e를 타이핑하면 e객체를 살펴볼 수 있다.
이벤트 객체 내에는 중요한 속성이 있다. 그것은 'target'이라는 속성인데, 이 e.target이라는 속성은 이벤트가 발생한 태그, 즉 여기서는 사용자가 클릭한 a태그를 가리킨다. 그래서 e.target을 이용해 클릭한 a태그를 알아내고 a태그에 지정해둔 data-id에 접근할 수 있게 만든다.

data-id값을 찾기 위해서는 'data-'라는 접두사로 시작되는 속성을 가진 dataset객체를 통해 접근 할 수 있다.
그렇다면 a태그가 data-id 속성을 가지고 있으므로 이 값을 통해 클릭한 id값을 알 수 있다.

this.props.onChangePage(e.target.datset.id)

그리고 위처럼 작성해서 이 정보를 onChangePage함수를 호출할때 인자로 전달하면 된다.

이제 TOC 컴포넌트의 props로 지정한 onChangePage 함수의 인자 값이 실제로 클릭한 id값인지 debugger을 통해 출력해보자.

// App.js 파일
... 생략 ...
class App extends Component {
	... 생략 ...
    return(
    	<div className="App">
        ... 생략 ...
        <TOC data={this.state.contents} onChangePage={function(id){
              debugger;
              this.setState({mode:'read',
              selected_content_id:Number(id)
            });
        }.bind(this)}></TOC>
        ... 생략 ...


이렇게 하고 두번째 목록을 클릭하면 id값이 잘 들어오는 것을 확인할 수 있다.

이제 이 값을 그대로 selected_content_id값으로 지정하면 된다. 인자로 전달되는 id는 문자열 형태이므로 이 값을 숫자로 바꾸기 위해 Number()함수를 사용해 숫자로 변경했다.

그리고 debugger; 코드를 삭제하고 클릭해보면 내용이 바뀌는 것을 확인할 수 있다.

마지막 2번, 각 링크를 클릭했을때 원하는 콘텐츠들이 나오도록 설정하는 것까지 완료했다. a태그의 onClick 이벤트를 실행시킬때 e.target.dataset.id에서 값을 추출하고, 그 값을 a태그의 속성인 data-id에서 가져왔다는 것을 잊지말자.

그리고 이제 두번째 방법을 설명하겠다.

두번째, bind()함수의 두번째 인자를 사용하는 것이다.

// TOC.js 파일
... 생략 ...

lists.push(<li key=data[i].id><a href={"/content/" + data[i].id} data-id={data[i].id} onClick={function(id, e){
	e.preventDefault();
    this.props.onChangePage(id);
}.bind(this, data[i].id)}>{data[i].title}</a></li>);

... 생략 ...

이 방법은 data-id 속성을 이용하지 않고, onClick 이벤트 함수에서 bind를 호출할때, 두번째인자로 "data[i].id" 값을 지정했다. 이렇게 하면 bind 호출 대상이 되는 함수의 첫번째 인자로 data[i].id값이 전달되고 나머지 인자는 한칸씩 뒤로 밀리게 된다.

원래는 첫번째 인자가 이벤트객체인 e이지만, bind의 두번째 인자를 주고 첫번째에 id를 입력하면 bind의 두번째인자값이 함수의 첫번째인자로 넘어가게 되어서 id값을 onChangePage에 그대로 넘길 수 있다.

이렇게 두번째 방법까지 정리해보았는데 자기한테 맞는걸 사용하면 될듯 하다.

오늘의 리액트 공부 끝~


출처: 생활코딩! 리액트 프로그래밍 책

profile
프론트엔드개발자가 되고 싶어서 열심히 땅굴 파는 자

0개의 댓글