[React.js - 02] Component
[React.js - 02] Component
컴포넌트
리액트 어플리케이션의 인터페이스 설계에서 사용자가 볼 수 있는 요소를 컴포넌트가 구성하고 있습니다. 컴포넌트는 단순한 템플릿이 아닙니다. 데이터가 주어지면 그에맞게 UI를 만들어주는것 뿐만 아니라 라이프사이클 API를 통해 컴포넌트가 화면에 나타나고 사라지고 변화가 일어날 때 작업을 처리할 수 있으며, 메서드를 통해 기능을 구현할 수 있습니다. 컴포넌트의 종류는 크게 두가지로 나뉩니다.
- 클래스형 컴포넌트
import React from 'react'; import './App.css'; class App extends Component { render() { return ( 클래스형 컴포넌트 ); } } export default App;
위와같이 클래스로 선언한 컴포넌트를 의미합니다. 클래스형 컴포넌트는 state, 라이프사이클 기능 그리고 임의 메서드를 정의할 수 있다는 점입니다. render() 함수가 반드시 필요하며 그안에서 JSX 를 반환해야 합니다.
클래스형 컴포넌트의 단점은 함수형 컴포넌트보다 메모리 자원과 결과물의 크기가 더 크다는 단점을 가지고 있습니다. 엄청난 크기의 차이도 아니라 엄청난 단점도 아닌거죠..
- 함수형 컴포넌트
import React from 'react'; import './App.css'; function App() { return 함수형 컴포넌트; } export default App;
위와같은 선언 방법이 함수형 컴포넌트 입니다. 함수형 컴포넌트는 클래스형 컴포넌트보다 메모리자원과 결과물의 크기가 작다는 장점과 선언이 편하다는 장점이 있습니다. 단점은 state, 라이프사이클 API를 사용할 수 었다는 점인데 v16.8업데이트 이후 Hooks라는 기능의 도입과 함께 해결되었다고 합니다.
리액트 공식 매뉴얼에서는 함수형 컴포넌트와 Hooks를 사용하라고 권장하고 있습니다. 그렇다고 클래스형 컴포넌트를 몰라도되는건 아니기 때문에 공부해두도록 합시다!!
import React from 'react'; import './App.css'; import MyComponent from './MyComponent'; const App = () => { return ; }; export default App;
import React from 'react'; const MyComponent = () => { return 나의 만든 컴포넌트; }; export default MyComponent;
위의 예제는 MyComponent.js 와 App.js를 함수형 컴포넌트로 선언하고(ES6의 arrow function), App에서 MyComponent를 호출한 예제입니다.
export default XXX 코드는 import 했을때 선언한 클래스를 불러오도록 설정하게 됩니다.
터미널에서 yarn start 로 렌더링이 이루어지는지 확인해봅시다.
props
props는 컴포넌트의 속성을 설정할 때 사용합니다. 부모 컴포넌트에서 자식컴포넌트의 props를 설정합니다.
import React from 'react'; import './App.css'; import MyComponent from './MyComponent'; const App = () => { return ; }; export default App;
import React from 'react'; const MyComponent = props => { return 내가 만든 컴포넌트 {props.name}; }; MyComponent.defaultProps = { name: 'test' }; export default MyComponent;
위 예제를 보면 MyComponent의 선언에서 props를 파라미터로 받는 함수형 컴포넌트를 만들어줍니다. 그리고 MyComponent를 호출하는 부모 컴포넌트에서는 name이라는 속성과 값을 전달해주면 MyComponent에서 props.name으로 값을 가져올 수 있습니다.
또한 defaultProps를 선언하면 부모에서 props를 전달하지 않아도 기본으로 설정된 props를 가지게 됩니다.
import React from 'react'; const MyComponent = props => { const { name, children } = props; return ( 내가 만든 컴포넌트 {name} 컴포넌트 사이에 들어간 내용 : {children} ); }; export default MyComponent;
위와같이 비구조화 할당 문법으로 (destructuring assignment) props의 내용을 각각 name과 children으로 해체해서 사용도 가능합니다. 위 코드를 좀더 편하게 수정해보면 아래와 같습니다.
import React from 'react'; const MyComponent = ({ name, children }) => { return ( 내가 만든 컴포넌트 {name} 컴포넌트 사이에 들어간 내용 : {children} ); }; MyComponent.defaultProps = { name: 'test' }; export default MyComponent;
이렇게 선언할때 파라미터를 비구조화 할당 문법으로 함수형 컴포넌트를 선언합니다. 많이 사용하는 방식이죠.
props를 설정하는 방법을 알아보았고 이번엔 props의 type을 검증해보도록 합시다. props의 검증은 propTypes 로 검증합니다.
*PropTypes와 defaultProps를 사용하는 이유는 프로젝트 협업에 있어서 어떤 props가 필요하고 어떤 타입이 필요한지 등 코드를 이해하는데 효율이 높아지기 때문에 필수는 아니라도 사용하는 습관을 들이는것이 좋을 것 같습니다. *
import React from 'react'; import PropTypes from 'prop-types'; const MyComponent = ({ name, favoriteNumber, children }) => { return ( 내가 만든 컴포넌트 {name} 컴포넌트 사이에 들어간 내용 : {children} 가장 좋아하는 숫자 : {favoriteNumber} ); }; MyComponent.defaultProps = { name: 'test' }; MyComponent.propTypes = { name: PropTypes.string, /*필수로 props 지정*/ favoriteNumber: PropTypes.number.isRequired }; export default MyComponent;
우선 prop-types를 import 해주고 해당 컴포넌트의 props의 타입을 정의합니다. 위와같이 설정하면 MyComponent의 name props는 string 타입을 전달해야 한다는 것을 의미합니다. 만약 부모에서 숫자로 전달하면 console에 경고메세지가 출력됩니다.
favoriteNumber의 경우 isRequired가 추가로 설정되어 있는데 이는 반드시 props를 전달해야 한다는 의미입니다. 마찬가지로 전달하지 않으면 경고메세지가 console에 출력됩니다.
예제 코드에서 보여드린 타입 외에도 많은 타입검사가 가능하니 앞으로 공부하면서 알아보도록 합시다.
이제 위의 예제를 클래스형 컴포넌트로 바꾸면서 클래스형 컴포넌트에서 props를 사용하는 방법을 알아보도록 합시다.
import React, { Component } from 'react'; import PropTypes from 'prop-types'; class MyComponent extends Component { /* 클래스 내부에서 defaultProps와 propTypes 설정 */ static defaultProps = { name: 'test' }; static propTypes = { name: PropTypes.string, favoriteNumber: PropTypes.number.isRequired }; render() { const { name, favoriteNumber, children } = this.props; return ( 내가 만든 컴포넌트 {name} 컴포넌트 사이에 들어간 내용 : {children} 가장 좋아하는 숫자 : {favoriteNumber} ); } } export default MyComponent;
우선 클래스형으로 컴포넌트를 선언합니다. 그리고 render함수에서 JSX를 반환해주고 비구조화 할당으로 props를 설정합니다. 위의 예제는 defaultProps와 propTypes 를 클래스 내부에서 설정했고 함수형 컴포넌트처럼 설정해도 상관없습니다.
state
props는 부모컴포넌트에서 설정하는 값이고 읽기전용으로만 사용가능한 반면, state는 컴포넌트 내부에서 바뀔 수 있는 값을 의미합니다. state는 두가지 종류가 있습니다. 클래스형 컴포넌트의 state, 함수형 컴포넌트의 useState 함수를 통한 state사용 입니다.
우선 클래스형 컴포넌트에서 state를 변경하는 예제를 살펴보겠습니다.
import React, { Component } from 'react'; class Counter extends Component { /* constructor(props) { super(props); this.state = { number: 0, fixtedNumber: 0 }; }*/ state = { number: 0, fixedNumber: 0 }; render() { const { number, fixedNumber } = this.state; return ( {number} 고정 : {fixedNumber} { this.setState({ number: number + 1 }); }} > +1 ); } } export default Counter;
우선 class constructor 함수로 state를 정의하는 방법이 있습니다. 주로 constructor에서 꺼내서 위의 코드처럼 작성하는게 일반적입니다. state값을 변경하기 위해서는 setState 함수를 사용해서 변경해야 합니다. 버튼을 누르면 state.number 값에 +1 을 하는 코드 입니다. state.fixedNumber 는 setState에서 어떤 연산도 하지않기 때문에 고정되어 있겠죠
// 클릭시 1이 두번 더해질것으로 예상 onClick={() => { this.setState({number : number + 1}); this.setState({number : this.state.number + 1}); }} //이렇게 preState로 기존 상태값에서 변경해줘야함 onClick={() => { this.setState(prevState => { return { number: prevState.number + 1 }; }); this.setState(prevState => ({ number: prevState.number + 1 })); }}
위 코드는 클릭시 setState(preState, props) 를 사용하는 이유입니다. state는 setState를 사용한다고 바로 바뀌는것이 아니라 위와같은 방법으로 preState를 사용해 기존 상태에서 변경을 해줘야 합니다.
onClick={() => { this.setState( { number : number + 1 }, () => { console.log("setState 호출 callback function") } ); }}
setState 호출 이후 callback 함수도 호출 할 수 있습니다.
리액트 버전이 16.8 업데이트 이후 함수형 컴포넌트에서도 state를 사용할 수 있게 되었습니다. useState를 사용하는 방식입니다.
import React, { useState } from 'react'; const Say = () => { const [message, setMessage] = useState(''); const onClickEnter = () => setMessage('안녕하세요'); const onClickLeave = () => setMessage('안녕히 가세요!'); const [color, setColor] = useState('black'); return ( 입장 퇴장 {message} {setColor('red');}}>red {setColor('green');}}>green {setColor('blue');}}>blue ); }; export default Say;
useState 에 초기값을 설정(형식은 자유, 문자, 숫자, 배열 객체 등..)합니다. 클래스형 컴포넌트와 달리 초기값이 객체가 아니어도 상관없습니다. useState를 호출하면 배열이 반환되는데 첫번째 원소는 현재상태, 두번째 원소는 setter함수입니다. [text, setText],[color, setColor] 이런식으로 사용합니다. useState는 한컴포넌트에서 여러번 사용해도 상관없습니다.
state를 사용할때 주의할점은 반드시 setState 함수를 사용하거나, useState의 setter함수를 통해 변경해야한다는 점입니다. 그렇게 하지않으면 값을 바꿔도 렌더링되지 않기때문입니다.
//이렇게 직접 접근해서 바꾸면 안됩니다. this.state.number = this.state.number + 1; this.state.array = this.array.push(2); const [object, setObject] = useState({a:1,b:1}); object.a = 2;
또한 Object나 배열을 업데이트 할때는 spread 연산자로 사본을 만들어 업데이트 해야합니다. 이유는 따로 정리하지않고 잘 정리된 포스팅의 링크를 첨부하겠습니다. 불변성을 유지해야 하는 이유!
이것으로 컴포넌트와 props, state 사용방법정리를 마치도록 하겠습니다.
참고자료 : 리액트를 다루는 기술/김민준/길벗
from http://yzzzzun.tistory.com/36 by ccl(A) rewrite - 2020-03-07 00:54:51
댓글
댓글 쓰기