본문 바로가기

WINK-(Web & App)/HTML & CSS & JS 스터디

[2023 신입부원 심화 스터디] 박지민 #마지막주 React.js 섹션 3~6

반응형

섹션 3. State

1. State 소개

- state는 props의 값에 따라 내부의 구현에 필요한 데이터

- props는 사용자가 component를 사용하는 입장에서 중요한 것

 

2. State 사용

class App extends Component {
  constructor(props) {
    super(props);
    this.state = { 
      Subject: { title: "WEB", sub: "World wid Web!" }
    }; 
  }
  render() {
    return (
      <div ClassName="App">
        <Subject>
          title={this.state.Subject.title}
          sub={this.state.Subject.sub}
        </Subject>
        <TOC></TOC>
        <Content></Content>
      </div>
    );
  }
}

export default App;
  • App.js가 내부적으로 사용된다면 state를 통해 사용
  • Component를 실행할 때 constructor 함수는 가장 먼저 실행되며 초기화를 담당
  • state 값을 subject 컴포넌트에 props 값으로 전달하고 싶을 때, 상위 컴포넌트의 state 값을 하위 컴포넌트의 props의 값으로 전달 가능

 

3. key

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      Subject: { title: "WEB", sub: "World wid Web!" },
      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 interactive" },
      ],
    };
  }
  render() {
    return (
      <div ClassName="App">
        <Subject>
          title={this.state.Subject.title}
          sub={this.state.Subject.sub}
        </Subject>
        <TOC data={this.state.contents}></TOC>
        <Content></Content>
      </div>
    );
  }
}

import React, { Component } from "react";

class TOC extends Component {
  render() {
    var lists = [];
    var data = this.props.data;
    var i = 0;
    while (i < data.length) {
      lists.push(<li key={data[i].id}><a href={"/contenst/" + data[i].id}>{data[i].title}</a></li>);
      i = i + 1;
    }
    return (
      <nav>
        <ul>
       	  {lists}
        </ul>
      </nav>
    );
  }
}

export default TOC;
  • TOC.js를 통하지 않고 내부 데이터의 목록을 바꾸기 가능
    • lists.push(<li><a href={"/contenst/" + data[i].id}>{data[i].title}</a></li>); 처럼 여러 개의  element를 자동으로 생성하는 경우에는 key라고 하는 props를 가져야 한다는 에러 메세지가 발생
    • App 컴포넌트의 내부 state를 TOC에 주입하는 것을 통하여 TOC의 내부 데이터를 바뀌게 할 것

 

섹션 4. 이벤트

1. 이벤트 state props 그리고 render 함수

  • props, state, event가 서로 상호작용하면서 어플리케이션의 역동성을 부여
  • react에서는 props나 state의 값이 바뀌면 해당되는 컴포넌트의 render()함수가 호출되도록 약속되어있음
  • render 함수는 어떤 html을 표시할 것인가를 결정
  • props나 state의 값이 바뀌면 해당 컴포넌트의 render 함수 재호출
    • html(화면)이 다시 그려짐
import React, { Component } from "react";
import TOC from "./components/TOC";
import Content from "./components/Content";
import Subject from "./components/Subject";
import "./App.css";

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      mode: "welcome",
      Subject: { title: "WEB", sub: "World wid Web!" },
      welcome: { title: "Welcom", 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 interactive" },
      ],
    };
  }
  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>
    );
  }
}

export default App;
  • mode 값에 따라서 다른 render()함수가 호출되게 함 (아직 직접 mode를 바꿔줘야함)

 

2. 이벤트 설치

  • Subject 컴포넌트 내용을 가지고 와서 App에 도입 후 이벤트를 구현
import React, { Component } from "react";
import TOC from "./components/TOC";
import Content from "./components/Content";
import Subject from "./components/Subject";
import "./App.css";

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      mode: "welcome",
      Subject: { title: "WEB", sub: "World wid Web!" },
      welcome: { title: "Welcom", 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 interactive" },
      ],
    };
  }
  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> */}
        <header>
          <h1>
            <a
              href="/"
              onClick={function (e) {
                e.preventDefault(); // 이벤트의 기본 동작 금지
              }}>{this.state.Subject.title}</a></h1>
          	  {this.state.Subject.sub}
        </header>
        <TOC data={this.state.contents}></TOC>
        <Content title={_title} desc={_desc}></Content>
      </div>
    );
  }
}

export default App;
  • 아직 Web을 클릭 했을 때 mode가 welcome으로 바뀌지 않음

 

3. 이벤트에서 state 변경하기

<header>
	<h1><a href="/" onClick={fuction(e) {
	  e.preventDefault(); // 이벤트의 기본 동작 금지
	  // this.state.mode = 'welcome'; -> this 값이 아무것도 셋팅 X
	  this.setState({
		mode:'welcome'
	  })
	}.bind(this)}>{this.state.subject.title}</a></h1>
    {this.state.subject.sub}
</header>
  • 이벤트 함수 내의 this의 값은 아무것도 셋팅되어 있지 않음 -> bind(this) 코드를 추가해주어야 함
  • 이벤트 함수 내에서 state 값 변경 (this.setState({변경값})) 전달
  • Web 클릭 -> mode 값이 welcome으로 바뀜

 

4. 이벤트 bind 함수 이해하기

  • render 함수 내에서 this는 컴포넌트 자신을 가리키는데, 함수 안에서 this는 아무것도 가리키지 않
  • this값을 확실히 하기 위해서 bind함수를 사용
    • bind() → 기존 함수를 복제하여 this 값을 지정 후 새로 만든다.

 

5. 이벤트 setState 함수 이해하기

  • 컴포넌트 생성할 때 state 값은 직접 변경 가능
  • 컴포넌트의 생성이 끝난 후에 동적으로 state의 값을 바꿀 때에는 this.state.mode = ‘welcome’;처럼 변경 불가
    • setState()함수를 통해 변경하고 싶은 객체를 전달하는 방식으로 변경해야 함

 

6. 컴포넌트 이벤트 만들기 1, 2, 3

import React, {Component} from 'React';
import TOC from "./components/TOC";
import Subject from "./components/Subject";
import Content from "./components/Content";
import './App.css';

class App extends Component {
	constructor(props) {
	  super(props);
	  this.state = {
		mode:'welcome',
		selected_contented_id:2,
		subject:{title:'WEB', sub:'World Wid Web!'},
		welcome:{title:'Welcome', desc:'Hi, 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 interactive'}
		]
	  }
	}
    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 = this.state.contents[i].title;
				_desc = this.state.contents[i].desc;
				break;
			  }
			  i = i + 1;
			}
		}
	return (
		<div className="App">
			<Subject 
				title={this.state.subject.title}
				sub={this.state.subject.sub}>
				onChangePage={fuction(){
					this.setState({mode:'welcome'});
				}.bind(this)}
			</Subject>
			<TOC 
				onChangePage={function(id){
				this.setState({
					mode:'read',
					selected_content_id:Number(id)
				  });
				}.bind(this)}
				data={this.state.contents}
			></TOC>
				<Content title={_title} desc={_desc}></Content>
			</div>
        ); 
    }
}

export default App;
import React, {Component} from 'React';

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>
		);
    }
}

export default Subject;
import React, {Component} from 'React';

class TOC extends Component {
    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-id={data[i].id} // data-""
					onClick={function(e){
						e.preventDefault();
						this.props.onChangePage(e.target.dataset.id); // dataset.""
					}.bind(this)}
				>{data[i].title}</a>
			</li>);
		i = i + 1;
	}
        return ( 
		  <nav>
			<ul>
			  {lists}
			</ul>
		  </nav>
        );
    }
}

export default TOC;
  • Subject, TOC 컴포넌트에 onChangePage라는 이벤트를 만
  • 해당 이벤트에 함수를 설치해주면 이벤트가 발생
    • props로 전달된 onChagePage라는 함수를 호출
  • e.target.dataset.id 를 통해 값을 꺼

 

섹션 5. Create 기능 구현

1. 베이스 캠프

- redux는 데이터를 각각의 컴포넌트들에 분산해서 보관하는 것이 아니라, 하나의 데이터 저장소가 있어서, 그와 관련된 모든 컴포넌트들이 알아서 바뀌게 함

 

2. create 구현 : 소개

- CRUD : Create, Read, Update, Delete

- Control.js 구현

import React, { Component } from "react";
class Control extends Component {
  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
            onClick={function (e) {
              e.preventDefault();
              this.props.onChangeMode("delete");
            }.bind(this)}
            type="button"
            value="delete"
          ></input>
        </li>
      </ul>
    );
  }
}

export default Control;

 

3. create 구현 : mode 변경 기능

- Control.js 추가 구현

// 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 
		  onChangePage={function(id){
		  	this.setState({
			  mode:'read',
			  selected_content_id:Number(id)
			});
		  }.bind(this)}
		  data={this.state.contents}
		></TOC>
		<Control onChangeMode={function(_mode){
			this.setState({
			mode:_mode;
		  });
		}.bind(this)}></Control>
		<Content title={_title} desc={_desc}></Content>
	</div>
);
// Control.js
import React, { Component } from 'react';
class Control extends Component {
		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 onClick={function(e){
						e.preventDefault();
						this.props.onChangeMode('delete');
					}.bind(this)} type="button" value="delete"></input></li>
				</ul>
			);
		}

 

4. create 구현 : mode 전환 기능

 

- App컴포넌트의 mode가 create가 될때, ReadContent가 CreateContent로 바꾸기

// App.js
render() {
	var _title, _desc, _article = null;
	if (this.state.mode === 'welcome') {
		_title = this.state.welcome.title;
		_desc = this.state.welcome.desc;
		_article = <ReadContent title={_title} desc={_desc}></ReadContent>;
		} 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 = this.state.contents[i].title;
			_desc = this.state.contents[i].desc;
			_article = <ReadContent title={_title} desc={_desc}></ReadContent>;
				break;
			}
			i = i + 1;
		}
	} else if (this.state.mode === 'create') {
		_article = <CreateContent></CreateContent>
	}
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 
		  onChangePage={function(id){
		  	this.setState({
			  mode:'read',
			  selected_content_id:Number(id)
			});
		  }.bind(this)}
		  data={this.state.contents}
		></TOC>
		<Control onChangeMode={function(_mode){
			this.setState({
			mode:_mode;
		  });
		}.bind(this)}></Control>
		<Content title={_title} desc={_desc}></Content>
	</div>
);
// ReadContent.js
import React, {Component} from 'React';

class ReadContent extends Component {
render() {
	return ( 
		<article>
			<h2>{this.props.title}</h2>
			{this.props.desc}
		</article>
		);
   	}
}

export default ReadContent;
//CreateContent.js
import React, { Component } from "react";

class CreateContent extends Component {
  render() {
    return (
      <article>
        <h2>Create</h2>
        <form>
        </form>
      </article>
    );
  }
}

export default CreateContent;

 

5. create 구현 : form

 

import React, {Component} from 'React';

class CreateContent extends Component {
    render() {
        return ( 
		  <article>
		     <h2>Create</h2>
			 <form action="/create_process" method="post"
			   onSubmit={function(e){
			     e.preventDefault();
			   }.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 CreateContent;

 

 

6. create 구현 : onSubmit 이벤트

// App.js
else if (this.state.mode === 'create') {
	_article = <CreateContent onSubmit={function(_title, _desc){
	}.bind(this)}></CreateContent>
}
import React, {Component} from 'React';

class CreateContent extends Component {
    render() {
        return ( 
		  <article>
		     <h2>Create</h2>
			 <form action="/create_process" method="post"
			   onSubmit={function(e){
			     e.preventDefault();
				 this.props.onSubmit(
				   e.target.title.value,
				   e.target.desc.value
				 );
			   }.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 CreateContent;

 

7. create 구현 : contents 변경, shouldComponentUpdate

  • 배열에 데이터를 추가하는 방법으로는 2가지가 있음
    • push → 원래 배열이 바뀜
    • concat → 원래 배열이 바뀌지 않음 - 새로운 배열 리턴
class App extends Component {
		constructor(props) {
			super(props);
			this.max_content_id = 3;
			...
    render() {
			...
				} else if (this.state.mode === 'create') {
					_article = <CreateContent onSubmit={function(_title, _desc){
					this.max_content_id = this.max_content_id + 1;
					var _contents = this.state.contents.concat(
						{id:this.max_content_id, title:_title, desc:_desc}
					)
					this.setState({
						content:_contents
					});
					}.bind(this)}></CreateContent>
				}
        return (
            <div className="App">
                <Subject 
				  title={this.state.subject.title}
				  sub={this.state.subject.sub}>
				  onChangePage={fuction(){
				  	this.setState({mode:'welcome'});
				  }.bind(this)}
				</Subject>
				<TOC 
				  onChangePage={function(id){
					this.setState({
					  mode:'read',
					  selected_content_id:Number(id)
					});
				  }.bind(this)}
				  data={this.state.contents}
				></TOC>
				<Control onChangeMode={function(_mode){
				  this.setState({
					mode:_mode;
				  });
				  }.bind(this)}></Control>
				  {_article}
			</div>
        ); 
    }
}

export default App;

 

섹션 6_Update & Delete 기능 구현

1. update 구현

//UpdateContent.js
import React, { Component } from "react";

class UpdateContent extends Component {
  render() {
    return (
      <article>
        <h2>Update</h2>
        <form
          action="/create_process" method="post" onSubmit={function (e) {
            e.preventDefault();
          }.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 UpdateContent;

 

2. update 구현 : form

  • inputFormalHandler 함수를 만들어서 코드를 간결하게 표현
//UpdateContent.js
import React, { Component } from "react";

class UpdateContent extends Component {
  constructor(props){
  	super(props);
    this.state = {
      title:this.props.data.title,
      desc:this.props.date.desc
    }
    this.inputFormHandler = this.inputFormHandler.bind(this);
   }
   
   inputFormHandler(e){
     this.setState({[e.target.name]:e.target.value});
   }
   
  render() {
    return (
      <article>
        <h2>Update</h2>
        <form
          action="/create_process" method="post" onSubmit={function (e) {
            e.preventDefault();
          }.bind(this)}
        >
          <p>
            <input 
            type="text" 
            name="title" 
            placeholder="title"
            value={this.state.title}
            ></input>
          </p>
          <p>
            <textarea onChange={this.inputFormHandler}
            name="desc" 
            placeholder="description"></textarea>
          </p>
          <p>
            <input type="submit"></input>
          </p>
        </form>
      </article>
    );
  }
}

export default UpdateContent;

 

3. update 구현 : state 변경

import React, {Component} from 'React';

class UpdateContent extends Component {
		constructor(props){
			super(props);
			this.state = {
				title:this.props.data.title,
				desc:this.props.date.desc
			}
			this.inputFormHandler = this.inputFormHandler.bind(this);
		}
		
		inputFormHandler(e){
			this.setState({[e.target.name]:e.target.value});
		}

    render() {
        return ( 
			<article>
				<h2>Update</h2>
				<form action="/create_process" method="post"
					onSubmit={function(e){ 
						e.preventDefault();
						this.props.onSubmit(
							this.state.id,
							this.state.title,
							this.state.desc
						);
					}.bind(this)} 
				>
				<input type="hidden" name="id" value={this.state.id}></input> 
				<p>
					<input 
						type="text" 
						name="title"
						placeholder="title"
						value={this.state.title} 
						onChange={this.inputFormHandler}
					></input>
				</p>
				<p>
					<textarea onChange={this.inputFormHandler}
						name="desc" 
						placeholder="description" 
						value={this.state.desc}></textarea>
				</p>
				<p>
					<input type="submit"></input>
				</p>
				</form>
			</article>
		);
    }
}

export default UpdateContent;

 

4. delete 구현

  • window.confirm → 사용자에게 진짜 삭제할 건지 확인
// App.js
class App extends Component { 
			...
			<Control onChangeMode={function(_mode){
				if(_mode === 'delete'){
					if(window.comfirm('really?')){ 
						var _contents = Array.from(this.state.contents);
						var i = 0;
						while(i < this.state.contents.length){
							if(_contents[i].id === this.state.selected_content_id){
								_contents.splice(i,1)
								break;
							}
							i = i + 1;
						}
						this.setState({
							mode:'welcome',
							contents:_contents
						});
						alert('deleted!');
						}
					} else {
						this.setState({
							mode:_mode;
							});
						}
					}.bind(this)}></Control>
					{this.getContent()}
        ); 
    }
}

export default App;
// Control.js
import React, { Component } from 'react';
class Control extends Component {
		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 onClick={function(e){
						e.preventDefault();
						this.props.onChangeMode('delete');
					}.bind(this)} type="button" value="delete"></input></li>
				</ul>
			);
		}
반응형