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

[Following Opentutorials] REACT, the Big CRUD~ part 1: create

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

Video summaries

  1. No summary yet.

  2. No summary yet.

https://www.youtube.com/watch?v=nwwJ2xU7E8w&list=PLuHgQVnccGMCRv6f8H9K5Xwsdyg4sFSdi&index=28

 

 

1. Create

1. Change the state.mode value for each CRUD action 

1) First, let's make the CUD list 

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 : 1,

      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>

          <ul>
           <li><a href="/create" >create</a></li>
           <li><a href="/update" >update</a></li>
           <li><input type="button" value="delete"></input></li>
         </ul>

         <Content title={_title} desc={_desc}></Content>
      </div>
    );
  }
}
export default App;

2) Extract the CUD list into a component

(1) Control.js

import React, {Component} from 'react';

class Control extends Component{
    render(){
      console.log('control render');
      return(
        <ul>
          <li><a href="/create" >create</a></li>
          <li><a href="/update" >update</a></li>
          <li><input type="button" value="delete"></input></li>
        </ul>
      );
    }
  }

  export default Control;

(2) app.js

 - At the top 

import Control from "./component/Control"

 - In the spot where <ul>...</ul> was, add the code below

<Control></Control>

 

3) Change the mode on every click 

It looks like you can go about it the same way as the earlier practice that changed the mode when clicking TOC or WRB. 

* A bump I forgot about: you have to do .bind(this) on both sides, heh

app.js  - set a custom event on the <Control> </Control> section and 

<Control onChangeMode={function(_mode){
  this.setState({
  	mode:_mode
  })
}.bind(this)}></Control>

control.js - here, bring in that event we set up and use it 

import React, {Component} from 'react';

class Control extends Component{
    render(){
      console.log('control render');
      return(
        <ul>
          <li><a href="/create" onClick={function(e){
            e.preventDefault();
            this.props.onChangeMode("create");
          }.bind(this)}>create</a></li>
          <li><a href="/update" onClick={function(e){
            e.preventDefault();
            this.props.onChangeMode("update");
          }.bind(this)}>update</a></li>
          <li><input type="button" onClick={function(e){
            e.preventDefault();
            this.props.onChangeMode("delete");
          }.bind(this)} value="delete"></input></li>
        </ul>
      );
    }
  }

  export default Control;

4) Create a new component and adjust things

(1) Adjust the existing <Content> block so it renders differently when mode is 'read' vs 'create'

 The <Content> </Content> spot 

becomes {_content} 

-  render(){ ...here... return(); }   

// In 'here', add a conditional for {_content} (if mode is create then~)

 (앞략)
 render(){
    console.log('APP render & this is : ' , this);
    var _title, _desc,_content = null;
    if(this.state.mode === 'welcome'){
      _title=this.state.welcome.title;
      _desc=this.state.welcome.desc;
      _content = <Read_Content title={_title} desc={_desc}></Read_Content>
    }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++; 
      }
      _content = <Read_Content title={_title} desc={_desc}></Read_Content>
    }else if(this.state.mode === 'create'){
      _content = <Create_Content></Create_Content>
    }
    
    return ( ... 중략 ...);
 }
 (말략 ㅋ)

- Create_Content.js — added the 'form tag' and tested an alert on submit.

For reference, onSubmit is the plain HTML spec, and the {function(){}} inside it is the React spec.
import React, {Component} from 'react';

class Create_Content extends Component{
    render(){
      console.log('Create render');
      return(
        <article>
          <h2>Create</h2>

          <form action="/create_process" method="post" 
            onSubmit={function(e){
              e.preventDefault();
              alert("submit");
            }.bind(this)}
          >
            <p><input type="text" name="title" placeholder="title"></input></p>
            <p><textarea name="desc" placeholder="description"></textarea></p>
            <p><input type="submit"></input></p>
          </form>
        </article>
      );
    }
  }
  export default Create_Content;

Since we called e.preventDefault();, if you click 'OK' on the alert and the page doesn't reload and just sits there quietly, it worked. 

(2) Now, on submit, add the created item to the TOC list ~

e.target points to the form itself 
- Write 'debugger;' below the alert line and refresh, and you can inspect the detailed properties of e.target. 

- First, pass the values up

app.js

On the <Create_Content></Create_Content> component we just created, add a custom event onSubmit={} to prepare to receive the values.

 

_content = <Create_Content onSubmit={function(_title, _desc){
  console.log(_title, _desc)
}.bind(this)}></Create_Content>

Create_Content.js 

Take the values from inside the form tag, pack them as arguments into the custom event onSubmit(title, desc), and hand them up to app.js.

//debugger;
this.props.onSubmit(e.target.title.value, e.target.desc.value);

- This time, add it to the UI list 

app.js

(---초략)
//*** 여긴 constructor(props){ ...여기(바로뒤에);  this.state={..} }

//1) contents의 수를 담은 변수를 추가하기(ui와 상관없으니, state 밖에 선언)
this.count_contents_id=3;

(---중략)

//*** 여긴 <Create_Content onSubmit={function(){ ...여기~... }}> 
//1) contents 맨뒤에 넣기 위해 +1 
this.count_contents_id=this.count_contents_id+1;
 
//2) 추가하기 - 아직 리액트는 모른다. 
this.state.content.push({
	id:this.count_contents_id, title:_title, desc:_desc
});

//3) 이제 리액트도 안다.
this.setState({
	content: this.state.content
});

(---말략)

- But, take two.      app.js 

A different way to create — push VS concat 
- When mutating state, concat is recommended because it doesn't modify the original and returns new data instead.
- It's also said to be better for later performance-improvement issues. 
import React, {Component} from 'react';
import './App.css';

import Subject from "./component/Subject"
import TOC from "./component/TOC"
import Control from "./component/Control"
import Read_Content from "./component/Read_Content"
import Create_Content from "./component/Create_Content"


class App extends Component{
  constructor(props){ 
    super(props); 

    this.count_contents_id=3;

    this.state={
      mode :"create", 
      welcome : {title:'welcome', desc:'hello react'},

      selected_content_id : 1,

      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,_content = null;
    if(this.state.mode === 'welcome'){
      _title=this.state.welcome.title;
      _desc=this.state.welcome.desc;
      _content = <Read_Content title={_title} desc={_desc}></Read_Content>
    }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++; 
      }
      _content = <Read_Content title={_title} desc={_desc}></Read_Content>
    }else if(this.state.mode === 'create'){
      _content = <Create_Content onSubmit={function(_title, _desc){
        //1
        this.count_contents_id=this.count_contents_id+1;

        //2-1
        // this.state.content.push({
        //   id:this.count_contents_id, title:_title, desc:_desc
        // });
        // this.setState({
        //   content: this.state.content
        // });

        //2-2
        var _new_content = this.state.content.concat({
          id:this.count_contents_id, title:_title, desc:_desc
        });
        this.setState({
          content: _new_content
        });

      }.bind(this)}></Create_Content>
    }
    
    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>
        <Control onChangeMode={function(_mode){
          this.setState({
            mode:_mode
          })
        }.bind(this)}></Control>
        {_content}
      </div>
    );
  }
}
export default App;

The result is the same as above~ 

 

1+a. 

1) push VS concat 

 

    (1) Don't mutate the original directly, work on a copy. 

    (2) With push, shouldComponentUpdate cannot be used.

 

2) shouldComponentUpdate(newPros, newState)

     (1) For TOC to render on screen, this.state.content[] gets reflected, 

         - but when the contents of content[] haven't changed, render() of content[] doesn't need to run, yet it keeps running.

     (2) If you add shouldComponentUpdate(){ return ...}

         it runs before render().

         - if the return is true, every render() runs; if false, render() doesn't run.

         shouldComponentUpdate can access both the newly changed value and the previous value.

         The developer can control whether render() runs based on the newly changed value and the previous value.

TOC.js

import React, {Component} from 'react';  

class TOC extends Component{
    shouldComponentUpdate(newProps, newState){
      console.log(" ======> shouldComponentUpdate",
        newProps.data,
        this.props.data
      );
      if(this.props.data === newProps.data){
        return false;
      }
      return true;
    }

    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;

 

2) immutable — immutability 

      (1) Leveraging features of Array

       If only immutability is the concern, you can pull it off with push too, without shouldComponentUpdate(). 

//2-1 + immutable 불변성

var _new_content = Array.from(this.state.content);

_new_content.push({
	id:this.count_contents_id, title:_title, desc:_desc
});

this.setState({
	content: _new_content
});

      (2) For objects,

            but, I tested it just in case... heh 

      (3) When it gets too fuzzy to keep switching back and forth —> you can also lean on a library!

              Every operation is immutable with respect to the original, which cuts down on confusion. 

https://github.com/immutable-js/immutable-js

 

immutable-js/immutable-js

Immutable persistent data collections for Javascript which increase efficiency and simplicity. - immutable-js/immutable-js

github.com

 

 

2. Update

https://www.youtube.com/watch?v=YKdebEty6uQ&list=PLuHgQVnccGMCRv6f8H9K5Xwsdyg4sFSdi&index=36

(ing)

 

 

 

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