[React] 컴포넌트

홍순범·2022년 1월 1일
0

React

목록 보기
2/3

리액트를 사용하여 인터페이스를 설계할 때 사용자가 볼 수있는 요소는 여러가지 컴포넌트로 구성되어 있다. 컴포넌트를 선언하는 방식은 함수 컴포넌트클래스형 컴포넌트 두 가지가 있다.

1. 클래스형 컴포넌트

import React, { Component } from 'react';

class App extends Component {
  render(){
    const name = 'react';
    return <div className='react'>{name}</div>;
  }
}

함수 컴포넌트와 차이점은 state 기능라이프사이클 기능을 사용할 수 있으며 임의 메서드를 정의할 수 있다. 함수 컴포넌트는 위의 기능을 사용하지 못하는 단점이 있었지만 리액트 v16.8 이후 Hooks라는 기능이 도입되면서 해결이 되었다.

클래스형 컴포넌트에서는 render()가 꼭 있어야 하고, 그 안에서 보여 주어야 할 JSX를 반환해야 한다.

작성한 컴포넌트는 export default '컴포넌트 이름' 을 사용해 다른 파일에서 사용할 수 있게 내보내기가 가능하며 import '컴포넌트 이름' from '파일이 위치한 경로' 를 아용해 불러와 모듈을 사용하는 것이 가능하다.

2. props

properties를 줄인 표현. 컴포넌트 속성을 설정할 때 사용하는 요소이다.

props를 렌더링 할 때는 { } 기호로 감싸 주면 되고 지정할 때는 사용하는 곳에서 <'컴포넌트이름' '전달 할 props 이름'= '전달 할 props 값' /> 으로 지정하면 된다.

`MyComponent.js`
const MyComponents = props => {
  return <div>{props.name}</div>; // props 렌더링 
};
export default MyComponent;

`App.js`
import MyComponents from './MyComponent';

const App = () => {
  return <MyComponents name='react' />; // props 값 지정
}

export default App;

기본 값을 지정하고 싶을 때 defaultProps를 이용해 기본 값을 지정할 수도 있다.

`MyComponent.js`
MyComponent.defaultProps ={ // props를 전달하지 않았을때 '기본 값'이 출력된다.
  name: '기본 값'
};

`App.js`
const App = () => {
  return <MyComponents />; // props를 전달하지 않았기 때문에 defaultProps에 설정 된 기본값이 출력 된다.
}

태그 사이의 내용을 보여주고 싶을 때 children을 사용하면 된다.

`MyComponent.js`
const MyComponents = props => {
  return <div>{props.name}, {props.children}</div>; 
  // 태그 사이의 내용이 children값을 사용해 보여준다.
};
`App.js`
const App = () => {
  return <MyComponents>children</MyComponent>; //태그 사이에 값을 넣으면 children 으로 값이 전달 된다.
}

ES6의 비구조화 할당 문법을 사용하면 내부 값을 바로 추출 할 수가 있어 props의 값을 더 짧은 코드로 사용할 수 있다.

`MyComponent.js`
const MyComponents = props => {
  const {name , children} = props;
// const MyComponents = ({name, children}) - 이렇게 사용해도 동일하게 나온다.
  return <div>{name}, {children}</div>; 
};

컴포넌트의 필수 props를 지정하거나 props의 타입을 지정할 때는 propTypes를 사용한다. 지정하는 방법은 MongoDB의 스키마를 정의하는 것과 유사하다. 조건에 맞지 않아도 출력은 되지만 콘솔창에 오류 메시지를 출력한다.

`MyComponent.js`
import PropTypes from 'prop-types';
(...)
 MyComponent.propTypes = {
 	name: PropTypes.string, // name 값은 반드시 문자열로 전달.
 	age: PropTypes.number.isRequired // age 값은 숫자이며 필수로 전달 되어야 하는 값이다.
(...)

defaultPropspropTypes는 클래스 내부에서 지정하는 방법도 있다.

`MyComponent.js`
import React, {Component} from 'react';
import PropTypes from 'prop-types';

class MyComponent extends Component {
  static defaultProps = {
    name: '이름'
  };
 static propTypes = {
   name: PropTypes.string,
   age: PropTypes.number.isRequired
 };
render(){
  const { name, age, children } = this.props;
  	return (...);
            }
}
export default MyComponent;

3. 클래스형 컴포넌트의 state

state는 컴포넌트 내부에서 바뀔 수 있는 값을 의미한다. props는 부모 컴포넌트가 설정하는 값이며, 컴포넌트 자신은 읽기 전용으로만 사용할 수 있다. 값을 바꾸려면 부모 컴포넌트에서 바꾸어 다시 전달해 주어야 하며 전달 받은 값을 직접 바꿀 수 없다.

리액트에는 클래스형 컴포넌트가 지니고 있는 state, 함수 컴포넌트에서 useState함수를 통해 사용하는 state, 이렇게 두 가지 종류의 state가 있다.

클래스형 컴포넌트에 state를 설정할 때는 constructor 메서드를 작성하여 설정하는데 이때 반드시 suepr(props)를 호출해 주어야 한다. 이 함수가 호출되면 현재 클래스형 컴포넌트가 상속받고 있는 Component 클래스가 지닌 생성자 함수를 호출해 준다.

constructor(props){
  super(props);
  this.state = {
    number: 0 // state의 초기값 설정
  };
}
render() {
  const { number } = this.state; // this.state로 state 조회
  return (
    <div>
    	<h1>{number}</h1>
    	<button
    	onClick={() => { // onClick를 통해 클릭 이벤트를 지정
    		this.setState({number: number+1}); // setState를 사용하여 state에 새로운 값을 지정.
  		}
    	>+1</button>
    </div>
  

state를 constructor에서 꺼내기(초기값 설정)

state의 초기값을 지정하기 위해 constructor 메서드를 선언해주었지만 선언하지 않고도 초기값을 설정할 수 있다.

`기존 방식`
constructor(props){
  super(props);
  this.state = {
    number: 0, // state의 초기값 설정
    fixedNumber: 0
  };
}
`constructor에서 꺼내기`
state = {
  number: 0,
  fixedNumber: 0
};

setState에 객체 대신 함수 인자 전달하기.

this.setState를 사용하여 state값을 업데이트할 때는 상태가 비동기적으로 업데이트 된다. 다음과 같이 this.setState를 두 번 호출하게 되어도 버튼을 클릭하면 숫자가 2가 아닌 1씩 더해진다.

onClick={() => {
  this.setState({number: number+1});
  this.setState({number: this.state.number +1});
}}

이에 대한 해결책은 객체 대신에 함수를 인자로 넣어 주는 방법이 있다. 아래와 같이 함수로 인자를 넣어주게 되면 값이 한 번에 2씩 증가 하는것을 볼 수가 있다.

`사용 방법`
this.setState((prevState, props) => {
  // prevState는 기존 상태, 업데이트 과정에 props가 필요 없다면 생략 가능
  return {
    // 업데이트 할 내용
  }
})

`실제 코드 적용`
<button onClick={() => {
  this.setState(prevState => {
    return {
      number: prevState.number + 1
    }
  });
  this.setState(prevState => ({
    number: prevState.number + 1
  }));
}}
// 두 개의 코드는 동일한 기능을 한다
>
  +1
</button>

그리고 업데이트 한 뒤 특정 작업을 하고 싶을 때는 setState의 두 번째 파라미터로 콜백(callback) 함수를 등록하여 작업을 처리할 수 있다. 아래와 같이 작성 후 코드를 실행하면 console 화면에 입력한 동작이 실행되는 것을 볼 수가 있다.

<button onClick={() => {
  this.setState({number: number+1}, () => {
    console.log('setState 호출');
    console.log(this.state);
  }
                );
}}
>
  +1
</button>

4. 함수 컴포넌트의 State

배열 비구조화 할당

16.8 버전 이후부터는 useState라는 함수를 사용하여 함수 컴포넌트에서도 state를 사용할 수 있게 되었는데 이 과정에서 Hooks이라는 것을 사용하게 된다.

Hooks를 사용하기 전, 배열 비구조화 할당 이 있는데 객체 비구조화 할당과 비슷하고 훨씬 깔끔하게 작성할 수 있다.

`기본 사용`
const array =[1,2];
const one = array[0];
const two = array[1];

`배열 비구조화 할당 사용`
const array = [1, 2];
const [one, two] = array;

// 둘 다 one = 1, two = 2 라는 값이 담긴다

useState 사용하기

클래스형 컴포넌트에서 state의 초기값은 객체 형태이지만 useState에서는 초기값의 형태는 자유롭다. useState 함수를 호출하면 배열이 반환되는데 첫 번째 원소는 현재 상태, 두 번째 원소는 상태를 바꾸어 주는 함수이다. 그리고 배열 비구조화 할당을 통해 이름을 자유롭게 정해 줄 수 있으며 한 컴포넌트에서 여러 번 사용해도 상관이 없다.

import React, { useState } from 'react'

const Say = () => {
  const [message, setMessage] = useState('');
  const onClickEnter = () => setMessage('안녕하세요!');
  const onClickLeave = () => setMessage('안녕히 가세요!');
  
  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: 'green' }} onClick={() => setColor('green')}>
          초록색
		</button>
		<button style={{ color: 'blue' }} onClick={() => setColor('blue')}>
          파란색
		</button>
	</div>
	);
};

export default Say;

5. state를 사용할 때 주의 사항

state의 값을 바꾸어야 할 때는 setState 혹은 useState를 통해 전달받은 세터 함수를 사용해야 한다. 다음 코드는 잘못된 코드이다.

`클래스형 컴포넌트`
this.state.number = this.state.number+1;
this.state.array = this.array.push(2);
this.state.object.value = 5;

`함수형 컴포넌트`
const [object, setObject] = useState({ a: 1, b: 2 });
object.b = 2;

배열이나 객체를 업데이트 할 때는 사본을 만들고 그 사본에 값을 업데이트한 후, 그 사본의 상대를 setState 혹은 세터 함수를 통해 업데이트 하면 된다. 객체에 대한 사본을 만들 때는 spread 연산자(...)를 사용하여 처리하고, 배열에 대한 사본을 만들 때는 배열의 내장함수들을 활용하면 된다.

`객체 다루기`
const object = { a: 1, b: 2, c: 3 };
const nextObject = { ...object, b: 4 };
// 사본을 만들어서 b의 값만 덮어 쓰기

`배열 다루기`
const array = [
  {id: 1, value: true},
  {id: 2, value: true},
  {id: 3, value: false}
  ];
let nextArray = array.concat({ id: 4 }); // 새 항목 추가
nextArray.filter(item => item.id !== 2); // id가 2인 항목 제거
nextArray.map(item => (item.id === 1 ? {...item, value:false} : item));
// id가 1인 항목의 value를 false로 설정
profile
초보개발자

0개의 댓글