섹션 3 - State 소개
1. state 소개
- state는 props의 값에 따라서 내부에 구현에 필요한 데이터들
2. state의 사용
class App extends Component {
constructor(props) {
super(props);
this.state = { // state에서 subject를 초기화
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 값으로 전달 -> 상위 컴포넌트(App)에서 하위 컴포넌트(Subject)로 값 전달
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>);
// 여러 개의 목록을 자동으로 생성할 때는 key를 지정해줘야 한다.
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(contents)를 TOC에 주입하는 것(data)을 통하여 TOC의 내부 데이터를 바뀌게 할 것
섹션 4 - 이벤트
1. 이벤트 state props 그리고 render 함수
- props, state, event가 서로 상호작용하면서 역동성을 부여한다.
- react에서는 props나 state의 값이 바뀌면 해당되는 컴포넌트의 render()함수가 호출되도록 약속되어있다.
- render 함수는 어떤 html을 표시할 것인가를 결정한다.
- state의 값이 바뀌면 해당 state 값을 가지고 있는 컴포넌트의 render 함수 재호출 → 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';
// -> 1. bind 함수가 없으면 this가 어떤 컴포넌트인지 모른다. 2. bind 함수 사용해도 state 변경 값 전달 불가
this.setState({
mode:'welcome'
})
}.bind(this)}>{this.state.subject.title}</a></h1>
{this.state.subject.sub}
</header>
- 이벤트 함수.bind(this) → 이벤트 함수 내의 this 컴포넌트 지정 (여기선 App 컴포넌트!)
- this.setState({변경값}) → 이벤트 함수 내에서 state 값 변경 전달
- Web 클릭하면 mode 값이 welcome으로 바뀜
4. 이벤트 bind함수 이해하기
- render 함수 내에서 this는 render 함수가 속한 컴포넌트를 가리키는데 함수에서는 this가 아무값도 없다.
- ex) 실습 과정에 있는 header 태그 안 이벤트 함수 속 this는 undefined이다.
- this값을 확실히 하 위해서 bind함수를 사용하는 것
- bind() → 기존 함수를 복제하여 this 값을 지정 후 새로 만든다.
https://devmoony.tistory.com/71
5. 이벤트 setState함수 이해하기
- 컴포넌트 생성할 때(constructor) state 값은 직접 변경 가능
- 컴포넌트의 생성이 끝난 후에 동적으로 state의 값을 바꿀 때에는 this.state.mode = ‘welcome’;처럼 변경 불가.
→ setState()함수를 통해 변경하고 싶은 객체를 전달하는 방식으로 변경해야함.
6. 컴포넌트 이벤트 만들기
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) // 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는 a태그를 가리킨다.
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 구현 : 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>
);
}
3. 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;
4. 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는 form 자체를 가리키고 있음
e.preventDefault();
}.bind(this)} // html fomr 태그가 가지고 있는 고유 기능
>
<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;
5. 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는 form 자체를 가리키고 있음
e.preventDefault();
this.props.onSubmit(
e.target.title.value,
e.target.desc.value
);
}.bind(this)} // html fomr 태그가 가지고 있는 고유 기능
>
<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 구현 : contents변경
- 배열에 데이터를 추가하는 방법으로는 2가지가 있다.
- push → 원래 배열이 바뀐다.
- concat → 원래 배열이 바뀌지 않는다. (새로운 배열 리턴)
class App extends Component {
constructor(props) {
super(props);
this.max_content_id = 3; // ui에 영향을 줄 값이 아니기에 state 값으로 하지 않는다.
...
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) // 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는 form 태그 자체를 가리키고 있음
e.preventDefault();
}.bind(this)} // html form 태그가 가지고 있는 고유 기능
>
<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
- inputFormHandler 함수를 만들어서 코드를 간결하게 표현
//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); // 이렇게 하면 이 함수를 쓸 때 bind 함수를 안적어도 된다.
}
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는 form 태그 자체를 가리키고 있음
e.preventDefault();
}.bind(this)} // html form 태그가 가지고 있는 고유 기능
>
<p>
<input
type="text"
name="title"
placeholder="title"
value={this.state.title}
// this.props.date.title로 하면은 리액트가 개입해서 readonly로 바뀌어 수정 불가
// but 아직 수정 불가 -> onChange를 써줘야한다!
></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는 form 자체를 가리키고 있음
e.preventDefault();
this.props.onSubmit(
this.state.id,
this.state.title,
this.state.desc
);
}.bind(this)} // html form 태그가 가지고 있는 고유 기능
>
<input type="hidden" name="id" value={this.state.id}></input>
// id가 사용자에게 보이지 않게 하기 위해 hidden 폼 사용
// change 일어날 일 x, 눈에 보이지 않기 때문에 onChange 필요 x
<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>
);
}
'WINK-(Web & App) > HTML & CSS & JS 스터디' 카테고리의 다른 글
[2023 신입부원 기초 스터디] 박승환 #8주차 마지막 JS (0) | 2023.07.03 |
---|---|
[2023 신입부원 심화 스터디] 박지민 #마지막주 React.js 섹션 3~6 (0) | 2023.07.02 |
[2023 신입부원 심화 스터디] 김윤희 #마지막주차 React.js 섹션3 ~ 7 #치타는힘드러 (0) | 2023.07.01 |
[2023 신입부원 기초 스터디] 이총명 #8주차 - JS 해치웠나..? (0) | 2023.07.01 |
[2023 신입부원 기초 스터디] 한승훈 #8주차 - 섹션6 (0) | 2023.06.30 |