[스터디]리액트를 다루는 기술 – 컴포넌트
클래스형 컴포넌트
// App.js import React, { Component } from 'react'; import './App.css'; class App extends Component { render() { const name = '리액트'; return <div className="react">{name}</div>; } } export default App;
클래스형 컴포넌트
- state 기능 및 라이프사이클 기능을 사용할 수 있다.
- render 함수가 있어야 한다.
- JSX를 반환해야 한다.
함수형 컴포넌트
- 선언하기 편리하다.
- 메모리를 적게 사용한다.
- 빌드한 후 배포할 때 결과물의 파일 크기가 더 작다.
// MyComponent.js import React from 'react'; const MyComponent = () => { return <div>처음 만들어본 컴포넌트</div>; }; export default MyComponent; // App.js import React from 'react'; import MyComponent from './MyComponent'; // 커스텀(?) 태그를 사용할 수 있다. const App = () => { return <MyComponent />; }; export default App;
props
- 부모 컴포넌트에서 넘겨주는 값
// MyComponent.js import React from 'react'; // 부모로부터 받은 값(props)을 출력 const MyComponent = props => { return <div>안녕하세요, 제 이름은 {props.name}입니다.</div>; }; export default MyComponent; // App.js import React from 'react'; import MyComponent from './MyComponent'; // MyComponent에 값을 주입한다. const App = () => { return <MyComponent name="홍길동" />; }; export default App;
// MyComponent.js import React from 'react'; const MyComponent = props => { return <div>안녕하세요, 제 이름은 {props.name}입니다.</div>; }; // 부모로부터 넘겨받은 값이 없을 때, 디폴트 값을 설정한다. MyComponent.defaultProps = { name: '기본이름' }; export default MyComponent; // App.js import React from 'react'; import MyComponent from './MyComponent'; // MyComponent에 값을 주입하지 않고 넘긴다. const App = () => { return <MyComponent />; }; export default App;
children
- 컴포넌트 태그 사이의 내용
// App.js import React from 'react'; import MyComponent from './MyComponent'; // '리액트'는 MyComponent의 children 값이다. const App = () => { return <MyComponent>리액트</MyComponent>; }; export default App; // MyComponent.js import React from 'react'; const MyComponent = props => { return ( <div> 안녕하세요, 제 이름은 {props.name}입니다. <br /> children 값은 {props.children} </div> ); }; MyComponent.defaultProps = { name: '기본이름' }; export default MyComponent;
propTypes를 통한 props 검증
// MyComponents.js import React from 'react'; import PropTypes from 'prop-types'; const MyComponent = ({ name, children }) => { return ( <div> 안녕하세요, 제 이름은 {name}입니다. <br /> children 값은 {children} </div> ); }; MyComponent.defaultProps = { name: '기본이름' }; // name의 타입을 문자열로 지정하였다. 숫자를 넘기면, 브라우저 console에서는 경고가 뜬다. // 하지만, 브라우저에서는 실행된다. MyComponent.propTypes = { name: PropTypes.string }; export default MyComponent; // App.js import React from 'react'; import MyComponent from './MyComponent'; // name에 숫자를 넘겨서 console에 경고 메시지가 나온다. const App = () => { return <MyComponent name={3}>리액트</MyComponent>; }; export default App;
// MyComponent.js import React from 'react'; import PropTypes from 'prop-types'; const MyComponent = ({ name, favoriteNumber, children }) => { return ( <div> 안녕하세요, 제 이름은 {name}입니다. <br /> children 값은 {children} 입니다. <br /> 제가 좋아하는 숫자는 {favoriteNumber} 입니다. </div> ); }; MyComponent.defaultProps = { name: '기본이름' }; // isRequired : 필수값으로 설정한다. 미입력시 console에 경고문구 출력 MyComponent.propTypes = { name: PropTypes.string, favoriteNumber : PropTypes.number.isRequired }; export default MyComponent; // App.js import React from 'react'; import MyComponent from './MyComponent'; const App = () => { return <MyComponent name={'1'} favoriteNumber={1}>리액트</MyComponent>; }; export default App;
클래스형 컴포넌트에서 prop 사용하기
// MyComponent.js import React, { Component } from 'react'; import PropTypes from 'prop-types'; // 클래스 컴포넌트 형태로 변경하였습니다. class MyComponent extends Component { render() { const { name, favoriteNumber, children } = this.props; // 비구조화 할당 return ( <div> 안녕하세요, 제 이름은 {name}입니다. <br /> children 값은 {children} 입니다. <br /> 제가 좋아하는 숫자는 {favoriteNumber} 입니다. </div> ); } } MyComponent.defaultProps = { name: '기본이름' }; MyComponent.propTypes = { name: PropTypes.string, favoriteNumber: PropTypes.number.isRequired }; export default MyComponent;
// MyComponent.js import React, { Component } from 'react'; import PropTypes from 'prop-types'; // defaultProps, propTypes는 클래스 내부로 옮길 수 있습니다. class MyComponent extends Component { static defaultProps = { name: '기본이름' }; static propTypes = { name: PropTypes.string, favoriteNumber: PropTypes.number.isRequired }; render() { const { name, favoriteNumber, children } = this.props; return ( <div> 안녕하세요, 제 이름은 {name}입니다. <br /> children 값은 {children} 입니다. <br /> 제가 좋아하는 숫자는 {favoriteNumber} 입니다. </div> ); } } export default MyComponent;
state
- 컴포넌트 내부에서 바꿀 수 있는 값
- props는 부모 컴포넌트에서 설정하며, 자식 컴포넌트는 읽기전용으로만 사용가능하다.
// Counter.js import React, { Component } from 'react'; class Counter extends Component { constructor(props) { super(props); // state의 초기값 설정 this.state = { number: 0, fixedNumber: 0 }; } render() { const { number, fixedNumber} = this.state; return ( <div> <h1>{number}</h1> <h2>바뀌지 않는 값 : {fixedNumber}</h2> <button // onClick 이벤트에 수행할 함수를 지정 onClick={() => { // setState를 이용해 값을 넣을 수 있음 this.setState({ number: number + 1 }); }} > +1 </button> </div> ); } } export default Counter; // App.js import React from 'react'; import MyComponent from './MyComponent'; import Counter from './Counter'; const App = () => { return <Counter />; }; export default App;
// Counter.js import React, { Component } from 'react'; // constructor를 사용하지 않고 state를 초기화 할 수 있다. class Counter extends Component { state = { number: 0, fixedNumber: 0 }; render() { const {number, fixedNumber} = this.state; return ( <div> <h1>{number}</h1> <h2>바뀌지 않는 값 : {fixedNumber}</h2> <button // onClick 이벤트에 수행할 함수를 지정 onClick={() => { // setState를 이용해 값을 넣을 수 있음 this.setState({ number: number + 1 }); // setState를 사용해도 state값이 바로 변경되지 않아 값이 +2씩 올라가지 않는다. this.setState({ number: number + 1 }); }} > +1 </button> </div> ); } } export default Counter;
// Counter.js import React, { Component } from 'react'; class Counter extends Component { state = { number: 0, fixedNumber: 0 }; render() { const { number, fixedNumber } = this.state; return ( <div> <h1>{number}</h1> <h2>바뀌지 않는 값 : {fixedNumber}</h2> <button // 한 번 클릭할 때마다 값이 2씩 증가한다. onClick={() => { // setState를 이용해 값을 넣을 수 있음 this.setState(prevState => { return { number: prevState.number + 1 }; }); // 바로 위 setState와 형태는 다르지만 동일한 기능을 하는 코드 this.setState(prevState => ({ number: prevState.number + 1 })); }} > +1 </button> </div> ); } } export default Counter;
// Counter.js import React, { Component } from 'react'; class Counter extends Component { state = { number: 0, fixedNumber: 0 }; render() { const { number, fixedNumber } = this.state; return ( <div> <h1>{number}</h1> <h2>바뀌지 않는 값 : {fixedNumber}</h2> <button // 한 번 클릭할 때마다 값이 1씩 증가한다. onClick={() => { // setState에 콜백함수를 추가할 수 있다. this.setState( { number: number + 1 }, () => { console.log('콜백함수 호출!!'); console.log(this.state); } ); }} > +1 </button> </div> ); } } export default Counter;
함수형 컴포넌트에서 useState 사용하기
// Say.js import React, {useState} from 'react'; const Say = () => { // useState의 배열 첫번째 요소는 현재상태, 두번째 요소는 setter로 구성된다 const [message, setMessage] = useState(''); const onClickEnter = () => setMessage('안녕하세요.'); const onClickLeave = () => setMessage('안녕히 가세요.'); return ( <div> <button onClick={onClickEnter}>입장</button> <button onClick={onClickLeave}>퇴장</button> <h1>{message}</h1> </div> ); } export default Say; // App.js import React from 'react'; import Say from './Say'; const App = () => { return <Say />; }; export default App;
// Say.js import React, {useState} from 'react'; const Say = () => { const [message, setMessage] = useState(''); const onClickEnter = () => setMessage('안녕하세요.'); const onClickLeave = () => setMessage('안녕히 가세요.'); // state는 여러 번 사용할 수 있다. const [color, setColor] = useState('black'); return ( <div> <button onClick={onClickEnter}>입장</button> <button onClick={onClickLeave}>퇴장</button> <h1 style={{color}}>{message}</h1> <button style={{color:'red'}} onClick={() => setColor('red')}> 빨간색</button> <button style={{color:'blue'}} onClick={() => setColor('blue')}> 파란색</button> <button style={{color:'green'}} onClick={() => setColor('green')}> 초록색</button> </div> ); } export default Say;
state를 사용할 때 주의사항
- state값을 바꿔야 할 때에는 setState 혹은 useState를 통해 전달받은 세터 함수를 사용해야 된다.
- 배열이나 객체를 업데이트 할 때는 사본을 만든 후 사본을 업데이트하고, 사본을 setState 또는 세터 함수를 이용해 업데이트 한다.
// 아래처럼 statate 값을 바로 바꿀 수 없다. (아래 코드는 오류발생) this.state.number = this.state.number + 1; this.state.array = this.array.push(2); // 아래와 같이 사본을 이용하여 값을 변경한다. const object = {a: 1, b: 2, c: 3 }; // 사본을 만든 후 b 값을 덮어쓴다. // '...' 은 spread 연산자이다. 객체의 사본을 만들 때 이용한다. const nextObject = { ...object, b: 4 }; console.log(object); // object: {a: 1, b: 2, c: 3} console.log(nextObject); // nextObject: {a: 1, b: 4, c: 3} const array = [ { id: 1, value: true }, { id: 2, value: true }, { id: 3, value: false } ]; let nextArray = array.concat({ id: 4 }); let array2 = nextArray.filter(item => item.id !== 2); let arrat3 = nextArray.map(item => (item.id === 1 ? { ...item, value: false } : item)); console.log(nextArray); // 0: {id: 1, value: true} // 1: {id: 2, value: true} // 2: {id: 3, value: false} // 3: {id: 4} console.log(array2); /* 0: {id: 1, value: true} 1: {id: 3, value: false} 2: {id: 4} */ console.log(arrat3); /* 0: {id: 1, value: false} 1: {id: 2, value: true} 2: {id: 3, value: false} 3: {id: 4} */
props와 state의 차이점
- props는 부모 컴포넌트가 설정하여 자식 컴포넌트에 전달한다.
- state는 해당 컴포넌트 자체적으로 가진 값으로 컴포넌트 내부에서 값을 업데이트할 수 있다.
- 참고 : 리액트에서 데이터의 흐름은 부모-> 자식으로 일방향이다.
최신 댓글