부스트코스 강의 웹 프론트엔드 시작하기 (리액트&리덕스)
중 'Ch3 리액트의 이벤트'를 정리한 내용이다.
event는 앱의 역동성을 만들어준다. 제목에 링크를 연결하고 그 링크를 클릭하면 welcome 메세지가 나오게 하고, 그 후 목차에 있는 것들을 클릭하면 클릭한 것의 컨텐츠가 본문에 출력되도록 만들어본다.
먼저 현재 페이지가 welcome페이지인지 읽기 페이지인지 구분하기 위해 state에 mode라는 값을 추가한다.
리액트에서는 state의 값이 바뀌면 그 state를 가지고 있는 컴퍼넌트의 render 함수가 다시 호출된다. 그 render 함수 하위의 컴퍼넌트들도 각각 render 함수가 호출되며 화면이 다시 그려진다.
render는 어떤 html을 그릴 것인가를 결정하는 함수이다.
props나 state의 값이 바뀌면 해당되는 컴퍼넌트의 render함수가 호출되도록 약속되어 있다 (화면이 다시 그려진다).
mode의 값에 따라 컴퍼넌트의 rendering 결과가 달라지게 render 함수에 조건문을 사용해준다. this.state.mode의 값이 welcome일 경우와 read일 경우를 구분해주었다.
그리고 Content 컴퍼넌트의 텍스트로 하드코딩 되어있는 부분도 _title, _desc로 바꾸어 welcome을 화면에 표시해준다.
event programming을 리액트에서 어떻게 할지 살펴본다.
< Subject> 컴퍼넌트안에 있는 링크를 클릭했을 때 < Subject> 바깥의 앱의 state를 변경하는 작업을 해본다.
먼저 < Subject> 컴퍼넌트의 내용을 App에 먼저 도입해서 < a>라는 링크를 클릭할 때 코드가 경고창이 뜨도록 onClick을 사용해 이벤트를 구현한다.
리액트는 onClick 이벤트가 실행될 때 함수의 첫번째 매개변수의 값으로 이벤트 객체 'e'를 주입해야한다.
state를 세팅한 작업과 event를 설치한 작업을 연결시킨다.
단순히 this.state.mode='welcome';을 추가해주면 에러가 발생하는데, 에러 발생에 두 가지 이유가 있다.
1) 이벤트가 호출될 때 실행되는 함수 안에서 'this'의 값은 컴퍼넌트 자기자신을 가르키지 않고 아무 값도 세팅되어 있지 않기 때문이다. event를 설치할 때 'this'의 값을 찾을 수 없어서 에러가 발생하면 함수가 끝난 직후에 'bind(this)'를 추가해주면 된다. 'bind(this)'를 추가하면 함수 안에서 this는 컴퍼넌트 자신을 가리키게 된다.
2) 리액트는 state의 값이 바뀐 사실을 모른다. 'this.state.~'가 아닌 'this.setState()'함수를 사용해야한다. 'this.setState()'함수를 호출할 때 mode를 welcome을 바꾸도록 수정한다.
state값을 직접 변경하면 안되고 setState()함수를 통해 변경해야하는 이유를 알아보도록한다.
처음 constructor()함수로 컴퍼넌트 생성이 끝난 다음에 동적으로 state의 값을 바꿀 때는 this.setState()라는 함수에 변경하고 싶은 값을 객체 형태로 넣어주어야한다. 그냥 바꿔주면 리액트 입장에서는 값이 바뀐 사실을 모르게 되기 때문이다.
항상 state값이 변경되면 setState()함수를 이용해서 바꿔야한다. constructor()함수에서는 상관없이 그냥 바꿔주어도 된다.
기존 코드는 < a>태그를 클릭했을 때 onClick 이벤트의 함수가 호출되며 < APP> 컴퍼넌트의 mode 값을 'welcome'으로 바꾸었는데 이번에는 < Subject> 컴퍼넌트 안에 onChangePage라는 이벤트를 만들어 이 컴퍼넌트 안에서 링크를 클릭했을 때 이 이벤트에 설치된 함수를 호출하도록 만드는 작업을 해준다.
< Subject> 컴퍼넌트에 onChangePage라는 이벤트를 만들어주고, < Subject> 컴퍼넌트는 페이지가 바뀔 때 컴퍼넌트의 사용자가 설정한 onChangePage 함수를 호출해주면 된다. onChangePage라고 하는 함수는 props 형태로 < Subject>에 전달된다.
링크를 클릭하여 onClick 이벤트가 발생할 때 함수가 실행된다. 함수가 실행될 때 첫번째 인자로 이벤트 인자가 전달되고, 'e'객체의 preventDefault()함수를 호출해 링크를 클릭했을 때 페이지가 바뀌는 것을 막는다. 이후 < Subject>컴퍼넌트의 onChangePage()함수를 호출한다.
< Subject> 컴퍼넌트에 onChangePage 라는 이벤트를 만들고 그 안에 함수를 설치했고, 이제 onChangePage 이벤트가 발생할 때 props로 전달되는 onChangePage()함수를 호출하면 된다.
마지막으로 onChangePage()함수가 < APP> 컴퍼넌트의 mode값을 welcome으로 바뀌도록 수정한다.
이번에는 글 목록을 클릭했을 때 < APP>컴퍼넌트의 state 'mode'를 'read'로 바꾸고 클릭한 글 목록에 해당하는 contents 내용이 본문에 나오도록 만들어본다.
< TOC> 컴퍼넌트에 onChangePage 이벤트를 생성하고 < TOC> 컴퍼넌트 안에서 props형태로 전달받게 만든다.
이어서 클릭한 컨텐츠가 본문에 표시되게 해준다.
< App> 컴퍼넌트의 state에 selected_content_id와 같은 이름으로 우리가 현재 선택된 content를 표시한다. 그 값으로 contents라는 객체에 있는 id값과 일치하는 selected_content_id 값과 일치하는 것을 본문에 표시한다.
반복문을 수행하며 현재 순번의 contetns의 data.id 값과 this.state.selected_content_id 값이 일치하는 경우 _title, _desc에 해당되게 해준다.
< TOC> 컴퍼넌트의 onChangePage 이벤트를 실행시키는 부분은 TOC.js에서 a태그의 onclick 이벤트가 발생했을 때 this.props.onChangePage()를 실행할 때 App.js의 onChangePage()함수를 실행시키는 것이다. 이 onChangePage()함수를 실행시킬 때 항목의 id값을 넘겨주도록 한다. < TOC> 컴퍼넌트의 a태그에 "data-id={data[i].id}" 속성을 주고 로드하면 해당 id값인 1,2,3을 확인할 수 있다.
onChangePage에 id라는 매개변수를 주고 이 id 값을 selected_content_id의 값으로 넣어준다. 이 때 넘어오는 데이터는 숫자형으로 바꿔주기 위해 Number()를 이용해준다.
속성을 이용하지 않고 하는 방법도 있는데, bind(this)의 두번째 인자로 data[i].id 값을 주면 onClick 이벤트에서 실행되는 함수의 첫번째 매개변수 값이 bind의 두번째 인자로 들어온 data[i].id가 된다.
//TOC.js
<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>
이렇게 하면 링크를 클릭할 때 마다 아래 본문의 컨텐츠가 변경되어 나타난다.