지금까지 컴포넌트화, props, state를 모두 이용하여 만든 페이지이다.
여기까지가 기본 내용이고 이제는 이벤트를 이용해볼 건데,
내가 할 것은 WEB을 클릭했을 때 맨 아래 부분이 "HTML ~" 이게 아니라
처음 기본페이지에 걸맞게 Welcome이 나오도록 만들어볼 예정이다.
그리고 ul-li로 만들어준 HTML, CSS 등등도 거기에 맞는 정보를 나타나게 할 것이다.
1. 링크 걸기
class Subject extends Component {
render() {
return (
<header>
<h1><a href="/">{this.props.title}</a></h1>
{this.props.sub}
</header>
);
}
}
간단해서 설명이 필요X
a태그를 걸어주고 WEB을 클릭하면 /로 이동하도록 만든다.
2. web을 누르면 보여줄 내용 작성
App.js
class App extends Component {
constructor(props) {
super(props);
this.state = {
mode:'welcome',
subject:{title:'WEB', sub:'world wider 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 exciting'}
]
}
}
...중략
내용이 바뀌기 위해서는 페이지의 구분이 필요하다.
즉, 현재 페이지가 'welcome'페이지인지, 'read'페이지인지 구분하도록 추가해준다.
mode:'welcome', // mode가 welcome인지 read인지에 따라 내용이 바뀌게 구분,
기본페이지가 welcome이라서 mode는 welcome이 디폴트가 된다.
welcome:{title:'welcome', desc:'hello, react'}, // welcome일때 화면에 뿌려줄 내용
3. if문으로 화면제어하기
App.js
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>
);
return은 화면상 보여줄 부분이고,
렌더링이 될때 조건에 따라 다르게 보여주기 위한 조건문을 사용한다.
즉 mode가 welcome이라면 welcome과 hello, react를 보여줄 것이고,
read라면 HTML과 HTML~ 을 보여줄 것이다.
그리고 Content쪽에서 {_title}, {_desc}로 받아주면
내용이 유동적으로 변화하게 된다. (앞서 변수를 쓰는 이유)
감이 오겠지만 welcome 단어는 WEB을 클릭했을 때의 내용의 키워드,
read 단어는 HTML,CSS등을 클릭했을때 보여줄 내용의 키워드이다.
4. 클릭에 따라 내용이 바뀌는 이벤트(WEB클릭) / 클릭이벤트 테스트
<Subject title={this.state.subject.title}
sub={this.state.subject.sub}
onChangePage={ function(){
alert("hello");
}}>
</Subject>
해당 구문은 WEB을 클릭했을때 hello가 뜨도록 해주는,
자바스크립트와 비슷한 친숙하고 쉬운 구문이지만 자바스크립트와는 달라서 외워야하는 부분이다.
우리가 할일은 WEB을 클릭했을때 내용이 나오는 거니까,
Subject 컴포넌트의 props로 전달되도록 onChangePage라는 이벤트를 만들어준다.
이 onchangePage라는 이름의 속성값으로 이벤트 함수를 만들어서
Subject 컴포넌트에서 호출할 수 있게 만드는 것이다.
이렇게 하면 hello!가 정상적으로 뜬다.
테스트가 끝났으니 이제 해야할 일은 클릭했을때 맨 아래의 내용이 welcome으로 뜨는 것이다.
그러면 위에서 만들어준 이벤트를 Subject 컴포넌트에 전달하기 위해 어떤 방식을 써야할까.
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>
);
}
}
a태그를 클릭했을때 props가 동작하도록 하는 구문이다.
{this.props.title}이라는 WEB을 클릭했을때,
this.props.onchangePage()를 함수방식으로 사용하게 되고,
해당 함수가 열리면서 hello라는 창이 뜨게 된다.
그리고 여기서 리액트에서 쓰는 몇가지 규칙이 있다.
리액트 규칙1. onclick이 아니라 onClick이다.
리액트 규칙2. 문자로 묶는게 아니라 중괄호로 묶음( onClick={}이다.)
리액트 규칙3. 리액트는 역동적이라서 매번 리로드가 되므로
그것을 방지하기 위해서는 e.preventDefault(); 를 넣어줘야한다.
리액트 규칙4. this구문을 쓰기 위해서는 bind(this)를 같이 써줘야한다.
여기까지가 클릭으로 hello를 뜨게 하는 순서이다.
5. 클릭에 따라 내용이 바뀌는 이벤트(WEB클릭) / mode변경 + 본문이 나오는 클릭이벤트
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 data={this.state.contents}></TOC>
<Content title={_title} desc={_desc}></Content>
</div>
)
hello로 테스트를 했으니, 이번에는 진짜로 값을 넣을 차례이다.
WEB을 클릭했을때 항상 'welcome'이 되어야 하므로
mode를 'welcome'으로 세팅해주는데 여기서도 공식이 있다.
리액트 규칙5.쉽게 생각할때처럼 this.state.mode = 'welcome'이 아니라,
this.setState({ mode:'welcome' })로 변경해주어야 한다.
6. 클릭에 따라 내용이 바뀌는 이벤트(목록 클릭) / mode가 바뀌는 클릭이벤트
이번에 할 내용은 글목록 중 하나를 클릭했을때 본문에 그에 알맞은 내용이 나오는 것이다.
App.js
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 data={this.state.contents}
onChangePage= { function(id) {
this.setState({
mode: 'read'
});
}.bind(this)}>
</TOC>
<Content title={_title} desc={_desc}></Content>
</div>
);
WEB클릭 이벤트와 마찬가지로, onChangePage 이벤트를 적용시켜서
클릭을 했을때는 mode가 read로 바뀌도록 만들었다.
그 다음에는 이 부분이 실행될 수 있도록 TOC 컴포넌트에 코드를 작성하는 것이다.
TOC.js
while(i < data.length) {
list.push(
<li key={data[i].id}>
<a href={"/content/"+data[i].id}
onClick={ function(e) {
e.preventDefault();
this.props.onChangePage();
}.bind(this)}
>{data[i].title}</a>
</li>);
i = i + 1;
}
뭔가가 많아 보이지만 차근차근하면 이해가 갈 수 있다.
우선 <a href="클릭했을때 이동할 주소" "클릭할때 동작될 부분">클릭할 대상<a/>
이렇게 나눠지므로,"클릭할때 동작될 부분"에 onClick함수와
리로딩을 막고, props 함수를 실행하라. 라는 코딩을 해주었다.
여기까지가 mode를 변경하는 것이고, 다음으로는 클릭대상에 따라 본문을 다르게 할 것이다.
7. 클릭에 따라 내용이 바뀌는 이벤트(목록 클릭) / 본문의 내용이 바뀌는 클릭이벤트(1)
앞서 화면의 제어를 위해 mode를 준 것처럼, ul-li의 목록에 따라
내용이 바뀔 수 있도록 제어하기 위한 변수를 state에 지정해줄 것이다.
이 변수를 통해 해당 li의 id값과 일치할 때 본문을 가져오는 방향으로 코딩하기로 한다.
App.js
this.state = {
mode:'welcome',
selected_content_id:2,
...중략
}
selected_content_id의 디폴트 값을 2번으로 지정해주었다.(목록중 CSS부분)
App.js
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 = i+1;
}
}
본문의 내용은 어디에서 바뀌었나 생각해보면,
render()에서 this.state.mode가 read일때 _title, _desc값을 넣어주었었다.
그러므로 이 자리에 while문을 추가해서 값이 맞을 때 본문을 넣으면 좋을 것이다.
차근차근 while문의내용을 살펴보면,
우선 this.state.contents.length로 콘텐츠의 총 길이를 가져온다.
현재 id 1~3까지 있는 중괄호가 3개니까 3을 가져올거고, i는 0,1,2까지 돌린다.
그리고 id,title,desc가 존재하는 하나씩의 중괄호를 data에 넣어서
해당 id가 위에서 지정한 selected_content_id와 맞을 때 본문이 변경되며
어차피 한 번만 본문의 내용을 보여주고 끝내면 되므로 break; 문을 써주었다.
여기까지해서 창을 띄워서 selected_content_id의 값을 바꿔주면 본문도 달라진다.
하지만 원하는 건 목록을 클릭했을때 selected_content_id의 값이 변하는 거니까
예상대로, 목록부분에서 selected_content_id에 대한 내용을 추가해주어야 한다.
8. 클릭에 따라 내용이 바뀌는 이벤트(목록 클릭) / 본문의 내용이 바뀌는 클릭이벤트(2)
그럼 어떻게 해야할까? App의 <TOC>와 TOC 컴포넌트는 매칭된다.
App의 onChangePage={}가 실행된다는 건,
연결된 TOC 컴포넌트의 props가 실행된다는의미이다.
그러면 App에서 TOC 컴포넌트로 인자값을 넘길 수도 있을 것이다.
TOC.js
while(i < data.length) {
list.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;
}
App.js
<TOC data={this.state.contents}
onChangePage={ function(id) {
this.setState({
mode: 'read',
selected_content_id:Number(id)
});
}.bind(this)}>
</TOC>
두개를 함께 봐야 이해가 쉬울 것 같다.
우선, 형태는
TOC.js의 this.props.onChangePage(인자) => onChangePage={ function(id) { selected_content_id:Number(id) );
이렇게 인자를 통해 id를 받아오는 형태가 된다.
그럼 인자를 넣어줄 TOC부터 시작.
TOC.js에서 중요한 두 개의 부분을 이해하고 넘어가야 한다.
data-id={data[i].id}
this.props.onChangePage(e.target.dataset.id);
e를 디버깅해보면 onClick이벤트를 해부(?)한 소스들이 총망라되어있는데,
그 중에서 약속된 부분을 살펴보면
data-id의 id는 곧 dataset.id의 id와 매칭이 된다.
그러니까 data-zz="hello"로 넣어주면 dataset.zz 이런 형식으로 넣어줘야 한다는 것
e를 디버깅할 때 들어가보면 dataset: DOMStringMap { id : "2"}로 나올 것이다.
여기서 받아온 2라는 인자값을
onChangePage={ function(2) { selected_content_id:Number(2) );
이렇게 받아오기 때문에 비로소 selected_content_id값이 2가 된다.
여기서 주의할 점은, state의 contents의 id에 저장된 값은 숫자 2인데,
selected_content_id값은 문자 2이기 때문에 Number()로 변환을 시켜준다.
여기까지 하면 이제 WEB을 클릭했을때는 welcome 모드가 되면서
해당 내용이 본문에 표시되고
HTML / CSS / JavaScript를 클릭했을때는 read 모드가 되면서
해당 내용이 본문에 표시되는 결과를 얻을 수 있게 된다.
'프로그래밍 > React' 카테고리의 다른 글
[React] state 사용하기 (다중속성) (0) | 2021.01.22 |
---|---|
[React] state 사용하기 (단일속성) (0) | 2021.01.22 |
[React] 컴포넌트별로 파일 분리하기 (0) | 2021.01.22 |
[React] props 사용하기 (0) | 2021.01.22 |
[React] 기본적인 컴포넌트 만들기 (0) | 2021.01.22 |