Back to feed
Renewal·서른의 생활코딩

[Following Opentutorials] React 16 events — the full package chapter

NS
normalstory
cover image
YouTube 영상 미리보기YouTube
external media
YouTube 영상 미리보기YouTube
external media

Video summaries

  1. No summary yet.

https://www.youtube.com/watch?v=kviidk347nU&list=PLuHgQVnccGMCRv6f8H9K5Xwsdyg4sFSdi&index=19

 

Goal of the lesson 

Make it so that when you click an item in the TOC, the matching content gets shown in the area below the list.

 

1. Implement an event that changes the Subject's content 

1. Understanding React's characteristics 

1) When state (or props) value changes the screen is redrawn.

- When state (or props) value changes, the render() of the component holding that state (or props) is called again. 
- render() <-- decides which HTML to draw.

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) To check render(), drop a console.log into the render() of Content/TOC/Subject.js 

console.log('TOC render');   //TOC.js의 예

 

2. Implementing it with a click event 

Instead of editing it directly in debug mode, change the state value when a button is clicked. 

1) React is not 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;

        // When you hit 'OK' on the alert, the whole page reloads, which goes against the spirit of 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. Change the mode value through a click event 

1) onClick={function(e){ ...here... }} — inside the event function, add code that changes the state (this.state.mode='welcome';) 

                               //  Error Cannot read property 'state' of undefined <--  here 'this' has no assigned value or component.  

 

2) onClick={function(e){ ... this.state.mode='welcome'; ... }.bind(this)}  

                             // No error, but the state doesn't actually change through the click event. 

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. Understanding the bind() function 

Inside render(){ ...a... } the 'this' points to the component itself, but
- Hypothesis 1) inside render(){ ... return(...b...); } the 'this' is undefined.  // <-- is there a way to check this? 
- Hypothesis 2) inside render(){ ... return( function(){...c...} ); } the 'this' is undefined.   // <-- is it a scope issue?

Code for testing (Chrome DevTools, 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

If you write function A(){ ... }.bind(arg), the arg value becomes the 'this' of A(). 

 

5.Why you should set/change values via setState() 

Why not change state directly ( this.state.mode='welcome' ) and instead use the function ( this.setState( ) )
-> If you just change it directly, React doesn't know the change happened. 

 

6. Turning from an event consumer into an event producer

Remove the temporary <header> tag and re-use the Subject component we had before

1) Run an EVENT the user (ourselves) declared

(1) Subject component — create an onChangePage event and put the function inside <-- becoming an event supplier!

<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 component — use theonChangePage event inside it   <-- using the event!

<a href ="/" onClick={ function(e){ 
     e.preventDefault();                     
// prevent reload
     this.props.onChangePage
();       // call the event (function) I created 
}.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;
  

        // When you click 'web', the alert appears, and clicking OK doesn't reload — the page doesn't change. 

 

2) Final version

(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) Result 

 

 

2. Change the Content shown per TOC item 

2. Passing events to and from TOC 

1)  When the TOC is clicked, change state mode to '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)  When the TOC is clicked, swap in the individual content 

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;

 

The event object has an important property: target. 

e.target  -> points to the a tag, and through it you can access specific values on the a tag.   = e.target.dataset.id

Now that we've checked target's properties, let's apply it —>  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;

Using it as an argument of bind() instead of relying on the target property 

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;

 

This English version was translated by Claude.

친절한 찰쓰씨
Written by
친절한 찰쓰씨

Pleasant Charles — UI/UX researcher at AIT. Keeping notes on design, planning, and slow days here since 2010.

More on the author's page

Keep reading

Renewal

Steadily, for the long haul, without burning out

Mar 31, 2026·9 min
Renewal

Tech-life balance

Feb 7, 2026·3 min
Renewal

Humanality, by Park Jeong-ryeol

Feb 7, 2026·11 min