https://www.youtube.com/watch?v=kviidk347nU&list=PLuHgQVnccGMCRv6f8H9K5Xwsdyg4sFSdi&index=19
수업의 목표
TOC(목차)를 클릭하면 클릭한 항목에 해당 콘텐츠가 목록 아래 영역에 출력될 수 있도록 할 수 있다.
1. Subject의 콘텐츠를 변경하는 이벤트 구현하기
1. React의 특징 이해하기
1) state(또는 props) 값이 바뀌면 화면이 다시 그려진다.
state(또는 props) 값이 바뀌면 그 state(또는 props)를 가지고 있는 컴포넌트의 render()가 다시 호출된다.
- render() <-- 어떤 html을 그릴것인가를 결정한다.
2) app.js
import React, {Component} from 'react';
import './App.css';
//분리된 컴포넌트들 import
import Subject from "./component/Subject"
import TOC from "./component/TOC"
import Content from "./component/Content"
//*컴포넌트들이 '들어가는(나열되는)' 영역
class App extends Component{
constructor(props){ //컴포넌트가 실행될때, render 보다 먼저 '초기화'하기 위해 선언, 그안에 초기화할 요소들을 넣는다.
super(props);
this.state={
mode :"read",
welcome : {title:'welcome', desc:'hello react'},
subject:{title:'WEB', sub:'world wide web !'},
content:[
{id:1, title:"HTML", desc:"HTML is HyperText "},
{id:2, title:"CSS", desc:"Css is for design "},
{id:3, title:"JS", desc:"javascript is for intrective "}
]
}
}
render(){
console.log('app 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.content[0].title;
_desc=this.state.content[0].desc;
}
return (
<div className="App">
<Subject title={this.state.subject.title} sub={this.state.subject.sub}></Subject>
<TOC data={this.state.content}></TOC>
<Content title={_title} desc={_desc}></Content>
</div>
);
}
}
export default App;
3) render()를 확인하기 위해 Content/TOC/Subject.js 의 render() 아래 콘솔로그를 넣어본다
console.log('TOC render'); //TOC.js의 예
2. 클릭 이벤트로 구현하기
디버그 모드에서 직접 수정하는 것이 아니라, 버튼을 클릭하면 state의 값 바꾸기
1) 리액트는 html이 아니다! onClick
<a href ="" onClick={function(){ alert('hi'); }}>{this.state.subject.title}</a>
app.js (1)
import React, {Component} from 'react';
import './App.css';
//분리된 컴포넌트들 import
import Subject from "./component/Subject"
import TOC from "./component/TOC"
import Content from "./component/Content"
//*컴포넌트들이 '들어가는(나열되는)' 영역
class App extends Component{
constructor(props){ //컴포넌트가 실행될때, render 보다 먼저 '초기화'하기 위해 선언, 그안에 초기화할 요소들을 넣는다.
super(props);
this.state={
mode :"read",
welcome : {title:'welcome', desc:'hello react'},
subject:{title:'WEB', sub:'world wide web !'},
content:[
{id:1, title:"HTML", desc:"HTML is HyperText "},
{id:2, title:"CSS", desc:"Css is for design "},
{id:3, title:"JS", desc:"javascript is for intrective "}
]
}
}
render(){
console.log('app 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.content[0].title;
_desc=this.state.content[0].desc;
}
return (
<div className="App">
{/* <Subject title={this.state.subject.title} sub={this.state.subject.sub}></Subject> */}
{/* 일단 가볍게 만들어보기 */}
<header>
<h1><a href ="" onClick={function(){
alert('hi'); //확인 버튼을 누르면, react와 다르게 리로드 된다.
}}>{this.state.subject.title}</a></h1>
{this.state.subject.sub}
</header>
<TOC data={this.state.content}></TOC>
<Content title={_title} desc={_desc}></Content>
</div>
);
}
}
export default App;
// 얼럿창의 '확인' 버튼을 누르면, react의 특성?에 반하게 페이지 전체가 리로드 된다.
2) preventDefault()
<a href ="" onClick={function(e){ e.preventDefault(); }}>{this.state.subject.title}</a>
app.js (2)
import React, {Component} from 'react';
import './App.css';
//분리된 컴포넌트들 import
import Subject from "./component/Subject"
import TOC from "./component/TOC"
import Content from "./component/Content"
//*컴포넌트들이 '들어가는(나열되는)' 영역
class App extends Component{
constructor(props){ //컴포넌트가 실행될때, render 보다 먼저 '초기화'하기 위해 선언, 그안에 초기화할 요소들을 넣는다.
super(props);
this.state={
mode :"read",
welcome : {title:'welcome', desc:'hello react'},
subject:{title:'WEB', sub:'world wide web !'},
content:[
{id:1, title:"HTML", desc:"HTML is HyperText "},
{id:2, title:"CSS", desc:"Css is for design "},
{id:3, title:"JS", desc:"javascript is for intrective "}
]
}
}
render(){
console.log('app 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.content[0].title;
_desc=this.state.content[0].desc;
}
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);
//debugger;
e.preventDefault();
}}>{this.state.subject.title}</a></h1>
{this.state.subject.sub}
</header>
<TOC data={this.state.content}></TOC>
<Content title={_title} desc={_desc}></Content>
</div>
);
}
}
export default App;
3. 클릭 이벤트를 통해 모드값 변경하기
1) onClick={function(e){ ...여기에... }} 이벤트 함수 안에 state 값에 대한 코드(this.state.mode='welcome';) 추가
// 에러 Cannot read property 'state' of undefined <-- 여기서의 this는 지정하는 값, 컴포넌트 가 없다.
2) onClick={function(e){ ... this.state.mode='welcome'; ... }.bind(this)}
// 에러는 안나는데 클릭에 대한 이벤트로 state가 변경되지는 않는다.
3) onClick={function(e){ ... this.setState({ mode:'welcome' }); ... }.bind(this)}
import React, {Component} from 'react';
import './App.css';
//분리된 컴포넌트들 import
import Subject from "./component/Subject"
import TOC from "./component/TOC"
import Content from "./component/Content"
//*컴포넌트들이 '들어가는(나열되는)' 영역
class App extends Component{
constructor(props){ //컴포넌트가 실행될때, render 보다 먼저 '초기화'하기 위해 선언, 그안에 초기화할 요소들을 넣는다.
super(props);
this.state={
mode :"read",
welcome : {title:'welcome', desc:'hello react'},
subject:{title:'WEB', sub:'world wide web !'},
content:[
{id:1, title:"HTML", desc:"HTML is HyperText "},
{id:2, title:"CSS", desc:"Css is for design "},
{id:3, title:"JS", desc:"javascript is for intrective "}
]
}
}
render(){
console.log('app 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.content[0].title;
_desc=this.state.content[0].desc;
}
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 data={this.state.content}></TOC>
<Content title={_title} desc={_desc}></Content>
</div>
);
}
}
export default App;
4. bind() 함수 이해하기
render(){ ...a... } 안에 있는 this 는 컴포넌트 자신을 가르키지만,
- 가설 1) render(){ ... return(...b...); } 안에 있는 this 는 undifined 이다. // <-- 확인할 수 있는 방법이 있나?
- 가설 2) render(){ ... return( function(){...c...} ); } 안에 있는 this 는 undifined 이다. // <-- 지역-전역의 문제인가?
테스트를 위한 코드 (크롬 개발자 도구, http://localhost:3000)
//* 원하는 값 세팅
var value = {name:'peter'}
//* this 출력 시도
function thisValue(){
console.log(this.name);
}
//* 결과 보기
thisValue();
// undefined
//* bind 함수에 원하는 값의 변수를 인자로 세팅
var bindValue = thisValue.bind(value);
// undefined
//* 함수를 답은 변수를 출력하여 결과보기
bindValue();
// peter
function A(){ ... }.bind(인자) 로 작성하면 인자 값이 A()의 this가 된다.
5.setState() 함수로 값을 지정/변경해야 하는 이유
스테이트 값을 직접 바꾸지( this.state.mode='welcome' ) 않고 함수를 이용( this.setState( ) )하는 이유
-> 그냥 직접 바꾸게 되면 리액트가 바뀐 상황을 알지 못한다.
6. 이벤트의 사용자에서 생산자로 거듭나기
임시로 사용한 <header> 태그를 지우고, 원래 사용하던 subject 컴포넌트 재사용하기
1) 사용자가 선언한 EVENT 를 실행해보기
(1) subject 컴포넌트에 onChangePage 이벤트를 만들고 그 안에 함수를 설치 <-- 이벤트 공급자 되기 !
<Subject title={...} sub={...} onChangePage={ function(){ alert('Hi hi hi~'); }.bind(this)} > </Subject>
app.js
import React, {Component} from 'react';
import './App.css';
//분리된 컴포넌트들 import
import Subject from "./component/Subject"
import TOC from "./component/TOC"
import Content from "./component/Content"
//*컴포넌트들이 '들어가는(나열되는)' 영역
class App extends Component{
constructor(props){ //컴포넌트가 실행될때, render 보다 먼저 '초기화'하기 위해 선언, 그안에 초기화할 요소들을 넣는다.
super(props);
this.state={
mode :"read",
welcome : {title:'welcome', desc:'hello react'},
subject:{title:'WEB', sub:'world wide web !'},
content:[
{id:1, title:"HTML", desc:"HTML is HyperText "},
{id:2, title:"CSS", desc:"Css is for design "},
{id:3, title:"JS", desc:"javascript is for intrective "}
]
}
}
render(){
console.log('this in render : ' , this);
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.content[0].title;
_desc=this.state.content[0].desc;
}
return (
<div className="App">
<Subject
title={this.state.subject.title}
sub={this.state.subject.sub}
onChangePage={function(){
alert('Hi hi hi~');
}.bind(this)}
>
</Subject>
{/* <header>
<h1><a href ="" onClick={function(e){
console.log(e);
e.preventDefault();
console.log('this in function : ' , this)
}}>{this.state.subject.title}</a></h1>
{this.state.subject.sub}
</header> */}
<TOC data={this.state.content}></TOC>
<Content title={_title} desc={_desc}></Content>
</div>
);
}
}
export default App;
(2) subject 컴포넌트에 있는onChangePage 이벤트 사용하기 <-- 이벤트 사용하기 !
<a href ="/" onClick={ function(e){
e.preventDefault(); // 재로드 방지
this.props.onChangePage(); // 내가 생성한 이벤트(함수) 호출
}.bind(this) } >
subject.js
import React, {Component} from 'react';
//컴포넌트(사용자정의 테그)를 만드는 코드영역
class Subject extends Component{
//class 안에서는 function을 생략할 수 있다.
render(){
console.log('sub 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;
// web을 클릭하자, 얼럿이 떳고, 확인을 클릭해도 리로드되지 않아, 페이지가 바뀌지 않는다.
2) 최종화
(1) setState( { mode: ' value ' } )
app.js
import React, {Component} from 'react';
import './App.css';
//분리된 컴포넌트들 import
import Subject from "./component/Subject"
import TOC from "./component/TOC"
import Content from "./component/Content"
//*컴포넌트들이 '들어가는(나열되는)' 영역
class App extends Component{
constructor(props){ //컴포넌트가 실행될때, render 보다 먼저 '초기화'하기 위해 선언, 그안에 초기화할 요소들을 넣는다.
super(props);
this.state={
mode :"read",
welcome : {title:'welcome', desc:'hello react'},
subject:{title:'WEB', sub:'world wide web !'},
content:[
{id:1, title:"HTML", desc:"HTML is HyperText "},
{id:2, title:"CSS", desc:"Css is for design "},
{id:3, title:"JS", desc:"javascript is for intrective "}
]
}
}
render(){
console.log('this in render : ' , this);
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.content[0].title;
_desc=this.state.content[0].desc;
}
return (
<div className="App">
<Subject
title={this.state.subject.title}
sub={this.state.subject.sub}
onChangePage={function(){
//this.state.mode = 'welcome';
this.setState(
{mode:'welcome'}
);
}.bind(this)}
>
</Subject>
<TOC data={this.state.content}></TOC>
<Content title={_title} desc={_desc}></Content>
</div>
);
}
}
export default App;
(2) this.props.onChangePage()
<a href ="/" onClick={function(e){ e.preventDefault(); this.props.onChangePage(); }.bind(this)}>
Subject.js
import React, {Component} from 'react';
//컴포넌트(사용자정의 테그)를 만드는 코드영역
class Subject extends Component{
//class 안에서는 function을 생략할 수 있다.
render(){
console.log('sub 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;
(3) 결과
2. TOC 리스트별 Content 내용 변경하기
2. TOC에 이벤트 주고 받기
1) TOC 클릭 시 State mode를 'read'로 바꾸기
(1) app.js
import React, {Component} from 'react';
import './App.css';
//분리된 컴포넌트들 import
import Subject from "./component/Subject"
import TOC from "./component/TOC"
import Content from "./component/Content"
//*컴포넌트들이 '들어가는(나열되는)' 영역
class App extends Component{
constructor(props){ //컴포넌트가 실행될때, render 보다 먼저 '초기화'하기 위해 선언, 그안에 초기화할 요소들을 넣는다.
super(props);
this.state={
mode :"read",
welcome : {title:'welcome', desc:'hello react'},
subject:{title:'WEB', sub:'world wide web !'},
content:[
{id:1, title:"HTML", desc:"HTML is HyperText "},
{id:2, title:"CSS", desc:"Css is for design "},
{id:3, title:"JS", desc:"javascript is for intrective "}
]
}
}
render(){
console.log('this in render : ' , this);
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.content[0].title;
_desc=this.state.content[0].desc;
}
return (
<div className="App">
<Subject
title={this.state.subject.title}
sub={this.state.subject.sub}
onChangePage={function(){
//this.state.mode = 'welcome';
this.setState(
{mode:'welcome'}
);
}.bind(this)}
>
</Subject>
<TOC data={this.state.content}
onChangePage={function(){
this.setState(
{mode : 'read'}
)
}.bind(this)}
></TOC>
<Content title={_title} desc={_desc}></Content>
</div>
);
}
}
export default App;
(2) this.props = onChangePage
TOC.js
import React, {Component} from 'react'; //<-- Component class를 로딩하기위해
//컴포넌트(사용자정의 테그)를 만드는 코드영역
class TOC extends Component{
//class 안에서는 function을 생략할 수 있다.
render(){
console.log('TOC render');
var lists=[];
var data = this.props.data;
var i=0;
while(i<data.length){
//Each child in a list should have a unique "key" prop.
lists.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;
}
return(
<nav>
<ul>
{lists}
</ul>
</nav>
);
}
}
export default TOC; // 외부에서 TOC 안에 있는 요소들을 가져다 쓸 수 있도록 해줌
2) TOC 클릭 시, 개별 콘텐츠 바꾸기
TOC.js
import React, {Component} from 'react';
class TOC extends Component{
render(){
console.log('TOC 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}
//var data-id = {data[i].id}
data-id = {data[i].id}
onClick={function(e){
debugger;
e.preventDefault();
this.props.onChangePage();
}.bind(this)}
> {data[i].title}
</a> </li>);
i = i+1;
}
return(
<nav>
<ul>
{lists}
</ul>
</nav>
);
}
}
export default TOC;
이벤트 객체에는 target 이라는 중요한 속성이 있다.
e.target -> a tag 를 가르키고 이를 통해 a tag의 특정 값에 접근할 수 있다. = e.target.dataset.id
target의 속성을 확인했으면 적용해보자 -> onChangePage(e.target.dataset.id)
<a href={"/content/"+data[i].id}
data-skdjfnsdkfj = {data[i].id}
onClick={ function(e){ e.preventDefault(); this.props.onChangePage(e.target.dataset.skdjfnsdkfj); }.bind(this) } >
TOC.js
import React, {Component} from 'react';
class TOC extends Component{
render(){
console.log('TOC 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}
//var data-id = {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> </li>);
i = i+1;
}
return(
<nav>
<ul>
{lists}
</ul>
</nav>
);
}
}
export default TOC;
app.js
import React, {Component} from 'react';
import './App.css';
import Subject from "./component/Subject"
import TOC from "./component/TOC"
import Content from "./component/Content"
class App extends Component{
constructor(props){
super(props);
this.state={
mode :"read",
welcome : {title:'welcome', desc:'hello react'},
selected_content_id : 0,
subject:{title:'WEB', sub:'world wide web !'},
content:[
{id:1, title:"HTML", desc:"HTML is HyperText "},
{id:2, title:"CSS", desc:"Css is for design "},
{id:3, title:"JS", desc:"javascript is for intrective "}
]
}
}
render(){
console.log('APP render & this is : ' , this);
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.content.length){
var data = this.state.content[i];
if(data.id === this.state.selected_content_id){
_title=data.title;
_desc=data.desc;
break;
}
i++;
}
}
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.content}
onChangePage={function(id){
this.setState({
mode : 'read',
selected_content_id : Number(id)
})
}.bind(this)}
></TOC>
<Content title={_title} desc={_desc}></Content>
</div>
);
}
}
export default App;
target 속성을 이용하지 않고, bind()의 인자로 활용하기
onClick={
function(id, num, e){
e.preventDefault();
this.props.onChangePage(id, num);
}.bind(this, data[i].id, 10)
}
TOC.js
import React, {Component} from 'react';
class TOC extends Component{
render(){
console.log('TOC 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-skdjfnsdkfj = {data[i].id}
onClick={function(id,e){
e.preventDefault();
this.props.onChangePage(id);
}.bind(this, data[i].id)}
> {data[i].title}
</a> </li>);
i = i+1;
}
return(
<nav>
<ul>
{lists}
</ul>
</nav>
);
}
}
export default TOC;
'새로워지기 > 서른의 생활코딩' 카테고리의 다른 글
[생활코딩따라가기] React CRUD 중 UD ~ go~ (0) | 2019.05.29 |
---|---|
[생활코딩 따라가기] REACT, 대망의 CRUD~ 중, create (0) | 2019.05.28 |
[생활코딩 따라가기] React 15 props와 state (0) | 2019.05.26 |
[생활코딩 따라가기] React 12~14 deep dive컴포넌트 (0) | 2019.05.26 |
[생활코딩 따라가기] React 10,11 컴포넌트 만들기 (0) | 2019.05.26 |
댓글