React로 무작정 개발하기 - 1
컴포넌트 생성
개발을 하기에 앞서서, React도 Vue와 마찬가지로 UI를 컴포넌트 단위로 나눠서 개발을 한다. 그러므로 컴포넌트를 만드는 것부터 알아보자(매우 쉬움)
먼저, src 폴더에 Components라는 폴더를 만들고, HelloWorld.js 라는 파일을 만들자.
import React, { Component } from 'react';
class HelloWorld extends Component {
render() {
return (
<div>
<h1>Hello World!</h1>
</div>
);
}
}
export default HelloWorld;
매우 심플하다. render() 함수의 몸체가 구현체가 된다. 여기서 주의해야할 점은, return()안에는 반드시 하나의 tag로 감싸져있어한다. 왜냐하면, 컴포넌트 하나가 결국 하나의 노드엘리먼트가 되는데, 노드 엘리먼트에서 루트엘리먼트가 두개가 되서는 안되기 때문이다. 실제로 return (<div></div><div></div>);
가 되면 build error가 나서 출력이 되지 않는다. 무조건 div tag로 감쌀 필요는 없지만, 반드시 하나의 노드 엘리먼트에서는 루트 엘리먼트가 하나여야한다.
만든 컴포넌트를 사용하는 방법도 간단하다. App.js에서 class를 import하여 태그형식으로 작성해주면 된다.
import React from 'react';
import logo from './logo.svg';
import './App.css';
import HelloWorld from './Components/HelloWorld'
function App() {
return (
<div className="App">
<HelloWorld></HelloWorld>
</div>
);
}
export default App;
변수 in React 위에서 React Component에서의 구현체 부분을 어떻게 사용하는지 알아보았다. 그렇다면 이 구현체(render함수)를 활용하는 방법을 알아보자.
실제로 구현체라는 표현은 저밖에 안쓸듯
구현체를 활용하는 방법에는 여러가지 있지만, 변수를 바인딩하는 것이 아마 가장 많이 쓰일 것이다.
React에서 변수는 크게 props, state 로 나눌 수 있다. props부터 알아보도록 하자.
props
props가 이미 익숙한 사람도 많을 것이다. Vue.js에서의 props와 동일한 역할을 한다. 부모 컴포넌트에서 자식 컴포넌트를 생성할 때 넘겨주는 값을 저장하는 곳이다. 부모 컴포넌트에 의존성이 있는 값들을 props에 저장한다고 생각하면 편하다.
HelloWorld.js(자식 컴포넌트)
import React, { Component } from 'react';
class HelloWorld extends Component {
static defaultProps = {
title: 'default_title'
}
render() {
return (
<div>
<h1>Hello World! { this.props.title }</h1>
</div>
);
}
}
export default HelloWorld;
App.js(부모 컴포넌트)
import React from 'react';
import logo from './logo.svg';
import './App.css';
import HelloWorld from './Components/HelloWorld'
function App() {
return (
<div className="App">
<HelloWorld title={"test"}></HelloWorld>
</div>
);
}
export default App;
코드에 설명을 추가하자면, App.js에서 HelloWrold의 title값에 "test"를 부여한채로 HelloWorld 컴포넌트를 생성한다. HelloWorld 에서 prop으로 설정해둔 title이라는 변수에 "test"가 들어가게 된다.
추가적으로, defaultProps라는 static 변수를 통해 props의 default값을 선언해둘 수 있다. props를 쓴다면 반드시 default값을 선언해두자, 만약 defaultProps를 설정해두지 않고 부모컴포넌트에서 <HelloWorld></HelloWorld>
로 컴포넌트로 생선한다면, 화면에는 Hello World! undefined 가 뜰 것이다.
props를 사용할 때는 주의해야할 점이 하나있다. 자식 컴포넌트에서 받은 props값이 자식 컴포넌트에서 변경이 됬다면, 자식 컴포넌트에서는 변경한 props값이 적용이 되고, 부모 컴포넌트에서는 아무런 변화가 생기지 않는다. 그렇다면, 생성단계에서 prop을 전달하기만 하면 끝인가? 그렇지는 또 않다. 전 상황과 반대로 부모 컴포넌트에서 props로 전달하는 변수가 변경됬다면 하위 컴포넌트에서는 다시 변경이 된다. 말이 복잡하긴한데. 코드로 설명을 하면 좀 더 간단하다.
상위 컴포넌트
import React, { Component } from 'react';
import Test from './Test';
class HelloWorld extends Component {
static defaultProps = {
title: 'default_title'
}
render() {
console.log(Test);
return (
<div>
<h1>outer component : prop value : { this.props.title }</h1>
<Test test={this.props.title}></Test>
</div>
);
}
}
export default HelloWorld;
하위 컴포넌트
import React, { Component } from 'react';
class Test extends Component {
static defaultProps = {
test: 'test'
}
render() {
return (
<div>
<h1>inner Component : prop value : { this.props.test }</h1>
</div>
);
}
}
export default Test;
상위컴포넌트는 바로 전 예제에서 만든 HelloWorld.js이다. Test라는 하위 컴포넌트를 만들어서
App.js(string data(ex: "check")) => HelloWorld.js(title
props) => Test.js(test
props) 로 props를 전달한다고 생각하자.
위와 같은 코드를 돌려보면, 아래와 같은 결과가 나온다.
outer component : prop value : check
inner Component : prop value : check
개발자도구를 이용하여 하위 컴포넌트(Test.js)의 test
props를 'kkk'로 변경해보자, 그렇다면 아래와 같은 결과가 출력된다. 즉 하위 컴포넌트에서의 전달받은 props의 조작은 하위컴포넌트 한정으로만 적용된다.
outer component : prop value : check
inner Component :prop value : kkk
자 그렇다면 이상태에서, 상위 컴포넌트(HelloWorld.js)의 title
props를 'zzz'로 변경해보자, 그렇다면 아래와 같은 결과가 출력된다. 즉 상위 컴포넌트에서 전달할 props의 값이 변경되면, 상위컴포넌트 + 하위컴포넌트 모두 적용된다.
outer component : prop value : zzz
inner Component :prop value : zzz
props라는 값은 생성하고 컴포넌트간 연결이 끊어지지는 않는다. Vue.js와 마찬가지로 부모 => 자식으로의 데이터 전달을 위해서만 사용될 수 있다. 위와 같은 사실이 대수롭지 않아 보이지만, 고차 컴포넌트 식으로 개발을 하다보면 props를 변경할 때마다 신경써줘야할 게 참 많아질수도 있겠다는 생각이 들었다..
state
state 이 친구 역시 component 내부에서 변수로 사용되는 녀석인데, props보다 조금 까탈스러운 녀석이다.
뭐랄까.. private memeber variable같은 느낌..?
이 state에 등록되어 있는 변수들은 반드시 setState()라는 메소드를 이용해서만 변경이 가능하다. 컴포넌트내에서 독립적이면서 동적으로 들고 있어야하는 변수들을 저장해두기 위해 주로 쓰인다고 한다.
import React, { Component } from 'react';
import Test from './Test';
class HelloWorld extends Component {
state = {
remainTime: 50
}
startTimer = () => {
setInterval(() => {
this.setState({
remainTime: this.state.remainTime - 1
})
console.log(this.state.reaminTime)
}, 500)
}
render() {
console.log(Test);
return (
<div>
<h1>남은 시간 : {this.state.remainTime}</h1>
<button onClick={this.startTimer}> start </button>
</div>
);
}
}
export default HelloWorld;
Counter는 너무 간단한 것 같아
너무흔한..
timer를 한 번 만들어 보았다. 사용하는 방법은 매우 간단하다. state라는 변수를 클래스에 선언해주고, 그 값을 바꾸기 위해서는 setState()를 사용해주면 된다.
다만, 이 state라는 변수에 복잡한 객체가 들어올 수 가 있다. 이러한 경우 처리하는 방법들을 알아보자
setState 더 잘 쓰기
공식 문서에 설명으로는 "setState()는 컴포넌트 state의 변경 사항을 대기열에 집어넣고, React에게 해당 컴포넌트와 그 자식들이 갱신된 state를 사용하여 다시 렌더링되어야 한다고 알립니다." 이렇게 설명하고 있다. React 내부에 repaint 이벤트를 발생시키기위한 trigger라고 생각한다. trigger기 때문에 바로 변화가 적용되지 않을 수도 있다고 공식문서에 나와있으니 참고할 필요가 있다.
바로 변화가 적용되지 않는 자세한 이유를 알고 싶다면. https://github.com/facebook/react/issues/11527#issuecomment-360199710 를 참고하면 된다.
누가 정리좀 해줬으면 ㅋㅋ
setState(updater[, callback])
함수의 원형을 보면 위와 같다.
updater에 해당하는 녀석은 (state, props) => stateChange
의 형태를 가지는 함수이다. state와 props를 이용해서 새로운 state를 리턴하는 함수라고 생각하면 될 것 같다. 기본 예제로 보여드린 예시는 함수형식이아니라 stateChange
단순히 변경된 state상태를 쓴것이라고 본다. callback
에 해당하는 건, state는 변경되었고, 리페인트가 되기 전에 수행되는 동작들을 선언하는 구간이다.
updater
가 (state, props) => stateChange
를 좀 더 자세히 보자면, setState()가 수행되기전의 state이기 때문에 변경직전의 state라고 생각하면 된다.
setState((prevState) => ({
testNum: prevState.testNum + 1
}))
보통 위와 같은 prevState로 써넣고 많이 쓰는듯하다 하핳
p.s.
lifecycle 부터 먼저했어야했는데.. .실수했다..
'Front_End > React' 카테고리의 다른 글
React로 준비? 좀 하고 개발하기 - 1 : React Hooks (0) | 2022.11.10 |
---|---|
React로 준비? 좀 하고 개발하기 - 0 : React Lifecycle (0) | 2022.11.09 |
React로 무작정 개발하기 - 2 (0) | 2019.12.03 |
React로 무작정 개발하기 - 0 (준비) (0) | 2019.11.10 |