본문 바로가기

항해99 WIL

항해99 4주차 WIL(State, Props, 리렌더링 발생 조건)

state

컴포넌트 내부에서 변경될 수 있는 값이다. props는 부모 컴포넌트가 설정하고, 컴포넌트 자신은 props를 바꾸지 못하는 특성이 있는 것과는 차이가 있다.

 

리액트에 존재하는 state의 종류는 2가지다.

  • 클래스형 컴포넌트가 가지고 있는 state
  • 함수형 컴포넌트가 useState라는 함수로 사용하는 state

컴포넌트 내부에서 변경하고 싶은 값이 있을 때, state를 활용한다.

 

클래스형 컴포넌트의 state

state의 이름과 초기값을 constructor(생성자)에서 설정하고, render() 함수에서 this.setState 함수를 통해 state를 변경해 줄 수 있다.

import React, {Component} from 'react';

class Counter extends Component{

	constructor(props){
    	super(props); // constructor 정의 시 반드시 필요하다.
        this.state = { // state의 초기값 설정 부분
        	number:0
        };
    }
    
    render(){
    
    	const {number} = this.state;
        return (
        	<div>
            	<h1>{number}</h1>
                <button
                	onClick={()=>{
                    	this.setState({number : number+1})
                    }}
                >
                	+1
                </button>
            </div>
         );
     }   
}

export default Counter;

브라우저에서 버튼을 누를 때마다, 숫자가 1씩 증가함을 확인할 수 있다. number라는 state가, button의 onClick 이벤트에 설정된 함수 내의 this.setState 함수가 +1씩 state를 바꾸고 있는 것이다. this.setState 함수에서 number+1이라고 정의해 둔 부분 때문에 작동할 수 있다.

 

state의 초기값을 설정할 때 constructor메서드를 사용하지 않고도 아래와 같이 간편하게 설정할 수 있다.

import Recat, {Component} from 'react';

class Counter extends Component{
	
    state = {
    	number: 0
    }
    
    render(){
    
    	const { number } = this.state;
        return (
        	<div>
            	<h1>{number}</h1>
                <button
                	onClick={()=>{
                    	this.setState({number : number+1})
                    }}
                >
                	+1
                </button>
             </div>
         );
     }
}

export default Counter;

this.,setState 함수 사용 시, 인자로 객체 대신 함수를 넘겨줄 수도 있다.

...
<button
	onClick={()=> {
    	this.setState(prevState => {
        	return{
            	number:prevState.number + 1
            };
        });
        ...

setState로 state의 값을 변경한 다음, 다른 특정한 작업을 하고자 할 때는 setState의 두 번째 파라미터에 콜백 함수(callback function)를 등록하면 된다.

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

this.setState 함수의 첫번째 파라미터에는 state값을 변경하는 코드를 넣었고, 두 번째 파라미터에는 화살표 함수 문법을 사용하여 콜백 함수를 지정한 모습이다. 마우스로 버튼을 누를 때마다, 개발자 도구의 콘솔란에 출력이 될 것이다.

 

지금까지 살펴본 코드는 클래스형 컴포넌트에 state를 사용하는 모습들이었다.

원래 함수형 컴포넌트의 경우 state를 사용할 수 없으나, 리액트 16.8 버전 이후에는 useState 함수를 사용하여 함수형 컴포넌트에서도 state를 사용할 수 있다. Hooks라는 것을 사용하게 된다.

 

배열 비구조화 할당하기

객체 비구조화 할당은 props의 값들을 쉽게 가져오기 위해 const { name, childern } = props와 같이 코딩하는 것을 말한다.

배열 비구조화 할당도 이와 비슷하다.

const array = [1, 2]
const [one, two] = array

one에는 array[0]값이, two에는 array[1]값이 추출되어 손쉽게 들어간다.

이와 같은 방식이 함수형 컴포넌트의 useState 함수에서 그대로 적용된다.

 

함수형 컴포넌트의 state-useState의 사용

함수형 컴포넌트에서 state를 사용하는 방법을 알아보자. import문에 다음과 같이 useState를 불러와야 가능하다.

import React, { useState } from 'react'

클래스형 컴포넌트에서는 state={} 값을 정하고, this.setState함수로 state의 값을 변경하는 방식이었다.

함수형 컴포넌트에서는 useState함수가 이 두 개의 역할을 같이 할 수 있게 해 주는데 방식이 살짝 다르다.

import React, {useState} from 'react'

const Say = () => {
	const [message, setMessage] = useState('초기값');
    const onClickEnter = () => setMessage('안녕!');
    const onClickEnter = () => setMessage('잘가!');
    return (
    	<div>
        	<button onClick={onClickEnter}>입장</button>
            <button onClick={onClickEnter}>퇴장</button>
            <h1>{message}</h1>
        </div>
    );
};

export default Say;
const [message, setMessage] = useState('초기값');

배열 비구조화 할당 문법으로 이해하면, useState함수를 호출할 시, 배열이 반환되며, 배열의 첫 번째 원소는 현재 상태, 두 번째 원소는 상태를 바꾸어 주는 함수가 된다. 그것이 각각 message와 setMessage에 들어가는 것이다.

 

즉, message는 현재 state의 상태가 저장되고, setMessage는 state를 바꾸어 주는 setter 함수가 된다.

 

코드를 작성할 경우 message에는 '초기값'이라는 문자열이 들어갈 것이고, setMessage는 그러한 state의 값을 변경하는 함수가 된다.(message, setMessage 등의 이름은 사용자 마음대로 정하면 되는 것이다.)

const onClickEnter = () => setMessage('안녕!');
const onClickEnter = () => setMessage('잘가!');

이것은 버튼 onClick함수를 사전에 정의해 놓은 부분이다. 화살표 함수 문법과 setMessage 함수를 사용하여 작성한 모습이다.

아래에 return문에서 버튼 각각에 onClick함수를 지정해 준다.

버튼을 누를 때마다, message에 있는 값이 안녕! 혹은 잘가!로 바뀔 것이다.

 

useState함수는 하나의 컴포넌트 안에서 여러 번 사용해도 상관없다. message라는 상태 외에도 다른 상태를 더 추가하여 관리할 수 있다.

import React, {useState} from 'react';

const Say = () => {
	const [message, setMessage] = useState('초기값');
    const onClickEnter = () => setMessage('안녕!');
    const onClickEnter = () => setMessage('잘가!');
    
    const [color, setColor] = useState('black');
    
    return (
    	<div>
        	<button onClick={onClickEnter}>입장</button>
            <button onClick={onClickleave}>퇴장</button>
            <h1 style=>{message}</h1>
            <button style = onClick={()=>setColor('red')}>RED</button>
        </div>
    );
};

export default Say;

새로운 state인 color를 관리하고, h1 태그의 텍스트 색깔을 버튼을 눌러 변경하는 기능을 추가한다.

h1의 style에 있는 color state 값이 들어가게 되고, 버튼의 onClick부분에 setColor함수를 등록하여 state를 바꿀 수 있도록 한다.

 

state의 값을 변경할 때는, 클래스형 컴포넌트일 경우 this.setState함수를, 함수형 컴포넌트일 경우 useState함수만을 사용하는 것이 포인트다.

 

props와 state의 차이

  • props와 state 모두 컴포넌트에서 사용 혹은 렌더링 할 데이터를 담고 있다.
  • props는 부모 컴포넌트 쪽에서 설정해 주는 것이다. 컴포넌트 자신이 변경할 순 없다.
  • state는 컴포넌트 자신이 직접 가지고 있는 값이고, 그 값의 변경도 내부에서 가능하다.

출처 : https://chanhuiseok.github.io/posts/react-6/

 

props

props는 부모 컴포넌트에서 자식 컴포넌트에 전달해주는 값을 의미한다.

/* App.js */
import React from 'react';
import MyCompoent from './MyComponet';

function App() {
	return (
    	<MyComponet name="React"/>
   );
}   
export default App;

<MyComponet name="React" />에서 name부분이 props를 설정하는 부분이다. name이라는 props를 지정하고, 그 값은 React가 된다. App.js는 MyComponent를 불러오는 부모 컴포넌트라 볼 수 있다.

그러면  그 값을 받아와서 사용하는 부분을 MyComponent.js에서 작성해보자. 중괄호로 감싸고, 아래와 같이 작성해보자.

/* MyComponent.js */
import React from 'react';

const MyComponent = (props) => {
	return <div>테스트 페이지, {props.name}</div>;
};

export default MyComponent;

브라우저에 바로 테스트 페이지, React라는 텍스트가 표시될 것이다.

타입스크립트에서는 아래와 같이 작성할 수 있다.

/* MyComponent.js */
import React from 'react';

interface Props {
	name: string;
}

const MyComponent: React.FC<props> = ({name}) => {
	return <div>테스트 페이지, {name}</div>;
};

export default MyComPonent;

부모 컴포넌트 쪽에서 props를 설정해주지 않을 경우, props의 기본값을 MyComponent 쪽에서 설정해 줄 수 있다.

/* MyComponent.js */
import React from 'react';

const MyComponent = props => {
	return <div>테스트 페이지, {props.name}</div>;
};

MyComponent.defaultProps = {
	name: '기본'
}

export default MyComponent;

 

MyComponent.defaultProps={} 내부에 직접 defaultProps 값을 설정해 줄 수도 있다.

 

하지만 함수형 컴포넌트의 경우, props의 기본값 설정을 위와 같이 하지 않아도 된다.

/* MyComponent.js */
import React from 'react';

const MyComponent = ({name = 'Chan'}) => {
	return <div>테스트 페이지, {name}</div>;
};

export default MyComponent;

구조 분해 할당을 하면서 기본값을 부여해 주는 것으로 대신할 수 있다.

 

컴포넌트 태그 사이의 내용을 보여주려면 children을 사용한다.

/* App.js */
import React from 'react';
import MyComponent from './MyComponent';

function App() {
	return (
    	<MyComponent>사이에 들어온 값</MyComponent>
     );
}

export default App;

<MyComponent> 태그 사이에 들어있는 값을 MyComponent에서 보여줄 수 있으려면 다음과 같이 {props.children}이라고 작성해야 한다.

/* MyComponent.js */
import React from 'react';

const MyComponent = props => {
	return (
    	<div>테스트 페이지, {props.name}
        <br/>
        children 값은 {props.children}
        </div>
     );
};

MyComponent.defaultProps = {
	name: '기본'
}

export default MyComponent;

브라우저에서 childern 값은 사이에 들어온 값이라고 표시됨을 확인할 수 있다.

props의 활용을 좀 더 편리하게 할 수 있다. ES6의 비구조화 할당 문법을 사용하는 것이다.

const { name, childern } = props; 와 같이 단축하여 작성할 수 있다.

/* MyComponent.js */
import React from 'react';

const MyComponent = props => {
	const { name, childern } = props;
    return (
    	<div>테스트 페이지, {name}
        <br/>
        childern 값은 {childern}
        </div>
     );
};

export default MyComponent;

아래와 같이 더욱 단축시킬 수 있다. MyComponent 함수의 파라미터에 가져다 놓을 수 있다.

/* MyComponent.js */
import React from 'react';

const MyComponent = ({ name, childern }) => {
	return (
    	<div>테스트 페이지, {name}
        <br/>
        childern 값은 {childern}
        </div>
    );
};

export default MyComponent;

리액트로 다양한 프로젝트를 진행할 때, 위와 같이 props값을 가져올 시 비구조화(구조 분해)할당 문법을 정말 많이 사용하는 것을 볼 수 있다. 

 

출처:https://chanhuiseok.github.io/posts/react-5/

렌더링

렌더링은 기본적으로 React가 DOM을 변경해야 하는지 여부를 알 수 있는 방법이다. 초기 렌더링은 두 가지 기본단계, 렌더링 단계와 커밋 단계에서 발생한다. React는 렌더링 단계에서 컴포넌트 트리의 루트부터 시작하여 하위 컴포넌트로 프로세스를 계속 진행한다. 각 컴포넌트를 통과할 때 React는 createElement메서드를 호출하고 컴포넌트의 JSX를 React 요소로 변환한 다음 해당 렌더링을 저장한다. 이 변환이 완료되면 React 요소는 커밋 단계로 넘어가며, 이 단계에서 ReactDOM 패키지를 사용하여 DOM에 적용된다.

 

3가지 리렌더링 발생 조건

Props 변경

State 변경

부모 컴포넌트 렌더링

 

Props 변경

Props 업데이트가 일어나면 리렌더링을 한다. Props가 변경되는 건 부모 컴포넌트의 State도 변경이 일어난다는 의미다. 부모 컴포넌트의 State 변경이 발생하면 Props도 업데이트되고, 모든 하위 컴포넌트에 대해 리렌더링이 발생한다.

 

State 변경

State 업데이트가 일어나면 리렌더링을 한다. 리엑트에서 State 값이 변경되면 관련 컴포넌트들을 전부 리렌더링 한다. 리엑트는 변화를 바로바로 감지하여 화면에 변경사항을 보여주기 때문이다.

 

부모 컴포넌트 렌더링

부모 컴포넌트가 렌더링을 하면 그 자식 컴포넌트들은 모두 리렌더링 한다. Props와 같은 원리다.

 

 

이번 주차에선 저번 주차에 제대로 정리 못했던 props와 state가 과제로 나와서 저번 주 내용을 다시 정리할 수 있는 좋은 기회였다. 거기다 비전공자로 프론트엔드를 공부하고 있는데 너무 모르는 게 많아서 같이 있는 조원의 블로그에서 HTML과 JS, React의 기초를 다시 공부하려 한다. 토요일엔 HTML과 JS를 읽고 쓰면서 헷갈리던 부분을 정리했다. 일요일엔 React의 기본과 정의를 정리하고 주특기 1주 차에 나온 개인과제와 시험문제를 가지고 다시 코드를 해체하면서 props와 state, useState, component, filter함수, map함수, submit 등을 다시 공부할 예정이다. 기초가 없는 나이지만 다른 블로그 설명과 구글링으로 공식 문서를 보면서 낯선 단어들이 많이 보이지만 하나씩 찾아가면서 React에 대해 일주일 전보다 더 적응할 수 있게 노력하려 한다. 진짜 힘들지만 이 공부에 미쳐보려고 한다. 앉아서 계속 손코딩하고 눈으로 계속 보면서 익숙해진다면 2주 차 알고리즘 때처럼 조금이라도 문제를 풀 수 있는 상태가 될 거라고 믿고 있다. 짜증이 나고 스트레스받으면서도 이걸 하는 이유는 내 인생의 터닝포인트를 만드는 과정이기에 더 노력하려 한다. 2달 남은 기간 동안에 공부하고 나서 더 나아진 모습으로 이 글을 다시 읽으면서 내 언어로 다시 써보고 싶다.