앞서 컴포넌트를 만드는 방법으로
- createClass
- React Component Class
- 함수형 컴포넌트
세가지 유형의 컴포넌트에 대해서 알아봤다. 이번에도 세가지 유형의 프로퍼티와 상태를 어떻게 관리하는지 알아보자
프로퍼티 검증
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| import { createClass } from 'react'
const Inventory = createClass({ displayName : "Summary", propTypes: { id: PropTypes.number, name: PropTypes.string, items: } getDefaultProps() { return { id: 0, name: '', items : [] } } ... })
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| import React, { Component } from 'react'; import PropTypes from 'prop-types';
class Inventory extends Component { const {id, name, items} = this.props
static propTypes = { id: PropTypes.number, name: PropTypes.string, items : (props, propName) => (typeof props[propName] !== 'string') ? new Error('문자열이어야 해용') (props[propName].length > 20) ? new Error('20자 이내어야 함.') : null } static defaultProps = { id: 0, name: '', items: [] }
render() { return ( <div> {items.map((item, i) => <span key={i}>{item}</span> )} </div> ); } }
Inventory.propTypes = { ... }; Inventory.defaultProps = { }
export default Inventory;
|
프로퍼티 검증 및 기본값 셋팅 방법은 클래스와 동일하게 처리해도 되고, 디폴트 인자를 지정해도 된다.
1 2 3 4 5 6 7 8 9 10
| const Inventory = ({id = 0, name = '', items = []}) => { return <div> ... </div> }
Inventory.propTypes = { ... } export default Inventory;
|
참조(ref)
엘리먼트와 함수와 상호작용을 해야할 경우 사용된다
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| import React, { Component } from 'react'; import PropTypes from 'prop-types';
class Inventory extends Component { constructor(props) { super(props) this.submit = this.submit.bind(this) } submit(e) { const { _title } = this.refs; e.preventDefault(); _title = ''; _title.focus(); }
render() { return ( <form onSubmit={this.submit}> <input ref="_title" /> {} <button>추가</button> </form>> ); } }
|
constructor가 추가됐다. 함수에서 this를 사용하려면 constructor를 재정의해서 메서드에 this를 바인딩
해줘야 한다. constructor를 재정의 해줬으므로 props 역시 Component의 생성자(super)를 통해서 다시 셋팅해주자.
createClass를 이용하면 this영역을 자동으로 바인딩 해준다.
그리고 this.refs
를 사용해서 정의한 ref값을 받아온다.
부모 컴포넌트로 데이터 전달
콜백을 이용하여, 부모에게 데이터를 전달해보자!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| const logInven = (title) => console.log(`아이디와 이름 : ${id} ${name}`) <Inventory onNewUser={logInven} />
... submit(e) { const { _title } = this.refs; this.props.onNewUser(_title.value) _title = ''; _title.focus(); }
static defaultProps = { onNewUser : f=>f }
|
함수형 컴포넌트 안에서는 this가 없으므로 this.refs를 사용할 수 없다. 대신 함수를 사용한다. 이 함수는 input 엘리먼트 인스턴스를 인자로 받는다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| const Inventory = ({onNewUser = f=>f}) => { let _title const submit = e => { e.preventDefault(); onNewColor(_title.value) _title = ''; _title.focus(); } render ( <form onSubmit={submit}> <input ref={input => _title = input} /> {} <button>추가</button> </form> ) }
|
상태(status)
프로퍼티(property)는 렌더링하고 나면 바뀌지 않는다! 바뀌는 데이터를 관리하기 위해서는 status
를 사용해야한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| const Dog = ({selected = true, onClick=f=>f}) => <div className={(selected)? "dog selected" : "dog"} onClick={onClick} </div> Dog.propTypes = { selected: PropTypes.bool, onClick: PropTypes.func } const DogHouse = createClass({ displayName: 'DugHouse', getDefaultProps() { return { totalDogs: 1 } }, // 이렇게 초기화 한다. getInitialState() { return { dogsSelected: 1 } }, change(dogsSelected) { this.setState({dogsSelected}) }, render() { const {totalDogs} = this.props const {dogsSelected} = this.state // props와 똑같은 방식으로 받는다. return ( <div className="dog-house"> {[...Array(totalDogs)].map((n, i) => <Dog key={i} selected={i<dogsSelected} onClick={() => this.change(i+1)} /> )} <p>{dogsSelected} / {totalDogs}</p> </div> ) } }
|
콜백함수를 이용하여 자식에게 change함수의 결과를 돌려받아 state값을 받는다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| ...
class DogHouse extends Component { static propTypes = { totalStars: PropTypes.number } static defaultProps = { totalStars: 5 } constructor(props) { super(props) this.state = { starsSelected: 0 } this.change = this.change.bind(this) } change(dogsSelected) { this.setState({dogsSelected}) } render() { const {totalDogs} = this.props const {dogsSelected} = this.state return ( <div className="dog"> {[...Array(totalDogs)].map((n, i) => <Dog key={i} selected={i<dogsSelected} onClick={() => this.change(i+1)} /> )} <p>{dogsSelected} / {totalDogs}</p> </div> ) } }
|
ES6 클래스에서는 constructor 내부에서 state값을 초기화해준다
프로퍼티로 상태 초기화 하기
입력 프로퍼티로 상태 변수를 초기화할 수 있는데, 다른 컴포넌트에서 재사용 가능한 컴포넌트를 만드는 경우이다.
1 2 3 4 5 6 7 8 9 10
| const DogHouse = createClase({ ... componentWillMount() { const { dogsSelected } = this.props if(dogSelected) { this.setState({dogSelected}) } } ... })
|
componentWillMount
라는 메서드를 통해 가능하다. 컴포넌트가 마운트될때 단 한번 호출된다.
1 2 3 4 5 6 7 8
| ... constructor(props) { super(props) this.state = { dogsSelected: props.dogsSelected || 0 } this.change = this.change.bind(this) }
|
명료하다!
그런데, 상태가 있는 컴포넌트의 수를 최소화하는 것이 유지보수 측면에서 좋기 때문에 이런 패턴은 최소화 하는 것이 좋다고 한다.
프로퍼티를 통해 상태를 초기화하기 때문에, componentWillRecevieProps 생애주기를 이용해서, 프로퍼티가 바뀔때 함께 자식 컴포넌트의 값이 바뀌도록 설정할 수 있다.
Reference
Comment and share