[React] State, Props

Hyun·2022년 1월 3일
1

React

목록 보기
3/22
post-thumbnail

💡State(상태)란?

  • 컴포넌트 내부의 동적인 데이터
  • 컴포넌트가 갖는 테마처럼 계속 값이 바뀔 동적인 데이터이고, 상태를 바꾸는 등의 관리는 이걸 가진 컴포넌트가 직접 관리하게 된다.
  • 현재 컴포넌트에서 생성, 변할 수 있는 데이터로 오직 state가 생성된 컴포넌트 내에서만 변경이 가능합니다.
  • 컴포넌트 종류에 따라 2가지 사용방식이 있다
    • 클래스형 컴포넌트에서는 컴포넌트 자체가 state를 지니는 방식으로 사용
    • 함수형 컴포넌트에서는 useState라는 함수, Hook을 통해 사용(useState을 예시로 밑에 설명할 예정이다)
    • 함수 내부에 선언된 변수


상태란 계속해서 동적으로 변화하는 특정 상태(=값)
상태(=값)에 따라 각각 다른 동작을 함

React에서는 '컴포넌트Component'라는 구성단위로 이루어진다.
컴포넌트는 구성요소라는 뜻인데, 프로그래밍에 있어 재사용이 가능한 각각의 독립된 모듈을 뜻한다.
(화면을 구성하는 요소들을 각각 만들고 그것을 하나로 합쳐 하나의 페이지가 되는 형식)
이 컴포넌트 또한 작게 쪼개서 작은 부분을 합쳐 하나의 컴포넌트로 만들 수 있다. 하나의 큰 컴포넌트 안에서 여러개의 작은 tree 구조처럼 될 수 있습니다. 큰 요소 안에 작은 요소가 있으니 부모 자식의 관계가 형성될 수있는 것이다.

컴포넌트는 자신의 출력에 다른 컴포넌트를 참조할 수 있습니다. 이는 모든 세부 단계에서 동일한 추상 컴포넌트를 사용할 수 있음을 의미합니다.


(예시) 함수형 컴포넌트에서 useState라는 함수

카운터를 가지고 예시를 들어보겠습니다. : >
src파일 안에 counter.js파일을 만들어 준 후 컴포넌트(=레고의 블록들)을 만든다.

//App.js

import Counter from "./counter";

const App = () => {
  return (
    <div>
      <Counter/>
    </div>
  );
}

export default App;

//counter.js

import React,{useState} from "react";

const Counter=()=>{
    //0에서 출발을 하고
    //+버튼을 누르면 1씩 증가함
    //-버튼을 누르면 1씩 감소함
    //count 상태
    const [count,setCount] = useState(0)
    const onIncrease = () => {
        setCount(count+1);
    };
    const onDecrease = () => {
        setCount(count-1);
    };
    return(
        <div>
            <h2>{count}</h2> 
            <button onClick={onIncrease}>+</button>
            <button onClick={onDecrease}>-</button>
        </div>
    )
}

export default Counter;

import React,{useState} from "react";
리엑트의 기능을 사용하려면 import를 사용해야하고, 그 중 useState메서드 기능을 이용하겠다는 뜻 (콤마로 구문하고 중괄호를 쓴다.)

h2태그에 원래 0을 넣었었는데, 버튼을 누를때마다 바뀌는값이므로 {count}라는 변수를 넣어줌

useState라는 리엑트의 매서드는 배열을 반환하고 배열의 비구조화 할당을 통해서 0번째 인덱스(=파라미터)인 count, 1번째 인덱스(=파라미터)인 setCount 라는 상수로 받아옴
이때, 0번째 인덱스(=파라미터) count는 상태의 값으로 사용이 되고, 1번째인덱스(=파라미터) setCount는 count라는 상태를 변화시키는 상태변화함수로써 사용이 됨.
useState메서드를 호출하면서 넘겨준 인자인 0은 count라는 상태를 만드는데에 초기값으로 사용이 된다.
즉, 0에서 출발 = useState(0)

간단히 말해서 useState로 상태를 만들면 count라는 이름으로 상태를 불러올 수 있고, setCount라는 상태로 값을 변화시킬 수 있다는 것

자바스크립트에서 온클릭함수를이런식으로 작성을 했는데
리엑트에서는 온클릭함수를 onClick={함수명}이렇게 쓴다.(카멜케이스와 중괄호를 쓴다는점!)


화면에 표시된 값이 실시간으로 바뀐다는 것은,
count의 상태가 바뀔때마다 app.js컴포넌트가 counter컴포넌트를 호출하고 반환받은 html을 화면에 표시하는 것이기 때문에 counter라는 컴포넌트(=함수)가 return을 다시한다(=화면에 새로그림) 라고 할 수 있다.

이런것을 리랜더링이라고 하는데 이것을 통해 알 수 있는것은 컴포넌트는 자신이가진 state가 변화하면 화면을 다시그려 리랜더링을 한다(=함수가 다시 호출된다.)

리엑트에서 어떤 컴포넌트가 가진 state가 바뀌면 그 컴포넌트가 리랜더링 된다.
그렇기때문에 우리는 실시간으로 버튼을 눌러서 값을 바꿀 수 있는 counter도 만들 수 있다

지금은 state하나만 만들었지만, 카운터 컴포넌트가 state를 두개를 가져도 상관이 없다. 하지만 state이름(count)와 state상태변화함수(setCount)는 결국 상수를 선언하는 것(const)이기 때문에 이름이 겹치면 안된다


💡Props란?

  • Immutable Data(변하지 않는 데이터)
  • props는 객체를 배울 때 이야기했었던 properties(속성)의 줄임말
    컴퍼런트에게 값을 전달하는 가장 기본적인 방법
  • 상위(부모컴포넌트)에서 하위(자식컴포넌트)로 데이터(값)을 전달할 때 사용
  • 읽기전용
  • 함수에 전달하는 매개변수

위에 만들었던 App컴퍼런트(상위)에서 counter컴퍼런트(하위)에게 counter의 초기값을 0이 아니라 App컴퍼런트가 전달하는 값으로 사용해달라고 하면?
이때 prop이라는 개념을 이용하면 된다

import React from "react";
import Counter from "./counter";

const App = () => {
  return (
    <div>
      <Counter initialValue={5}/>
    </div>
  );
}

export default App;

자식컴퍼런트(counter)에게 initialValue라는 이름을 붙여서 값을 전달할 수 있다.

이런식으로 부모컴퍼런트인 App컴퍼런트에서 자식컴퍼런트인 counter컴퍼런트에게 어떤값에 이름을 붙여서 전달하는 방식을 prop이라고 이야기하고, 전달되는 데이터들을 복수형으로 이야기하면 props라고 할 수 있다.

import React,{useState} from "react";

const Counter=(props)=>{
    console.log(props);

    const [count,setCount] = useState(0)

    const onIncrease = () => {
        setCount(count+1);
    };
    const onDecrease = () => {
        setCount(count-1);
    };
    return(
        <div>
            <h2>{count}</h2> 
            <button  onClick={onIncrease}>+</button>
            <button onClick={onDecrease}>-</button>
        </div>
    );
};

export default Counter;

값을 prop을 통해 전달을 했는데, 전달만한다고 초기값으로 5가 나타나는것이아니다.
counter컴포넌트로가서 부모에게 내려받은 props를 const Counter=(props)=>{}매개변수를 통해서 받아서 사용할 수 있다.
제대로 받았는지 console.log(props);콘솔에 출력을 해보면 {initialValue: 5}객체안에 담겨서 값이 나오는 것을 확인 할 수 있다.

import React from "react";
import Counter from "./counter";

const App = () => {
  return (
    <div>
      <Counter a={1} b={2} c={3} initialValue={5}/>
    </div>
  );
}

export default App;

객체들을 위와같이 추가를해서 보내도 되는지 콘솔로 확인을 해보면
{a: 1, b: 2, c: 3, initialValue: 5} props를 몇개를 보내도 객체안에 담아서 나오는 값을 확인 할 수 있다.

코드를 아래와같이 깔끔하게 다시 짤 수 있다

import React from "react";
import Counter from "./counter";

const App = () => {
  const counterProps ={
    a : 1,
    b : 2,
    c : 3,
    d : 4,
    initialValue : 5
  }
  return (
    <div>
      <Counter {...counterProps}/>
    </div>
  );
}

export default App;

자식컴포넌트에게 전달해야 할 props들을 객체로 만들어서 객체를 펼쳐서 전달하는 스프레드연산자{...이름명}를 통해 전달을 해도 똑같이 자식컴포넌트가 받아서 사용가능하다.

import React,{useState} from "react";

const Counter=(props)=>{
    const [count,setCount] = useState(props.initialValue)

    const onIncrease = () => {
        setCount(count+1);
    };
    const onDecrease = () => {
        setCount(count-1);
    };
    return(
        <div>
            <h2>{count}</h2> 
            <button  onClick={onIncrease}>+</button>
            <button onClick={onDecrease}>-</button>
        </div>
    );
};

export default Counter;

props를 counter컴포넌트에서 꺼내쓰려면
props를 매개변수로 받고, 점표기법을통해서 사용할 수 있다(props.initialValue)

또, 객체를 스프레드연산자로 전달을 했고 받는쪽에서는 똑같이 객체로 받는다고 했으니까 비구조화할당을 통해서 받을수도 있다(props객체에서 initialValue값만 꺼내 쓴 것)
const Counter=({initialValue})=>{ const [count,setCount] = useState(initialValue)}


만약, initialValue값을 쓰려고하는데 부모컴포넌트에서 값을 없애버렸다면 undefined오류가 일어난다.

이를 해결하려면 counter컴포넌트에 Counter.defaultProps={
defaultProps : 0
};

defaultProps를 설정을 해주면 된다.

defaultProps기능을 사용하면 전달받지 못했던 props의 기본값을 설정해서 에러를 방지할 수 있다.

🌟props는 자식컴포넌트에게 정적인 데이터뿐만아니라 동적인 데이터도 전달할 수 있다.

//OddEvenResult.js

const OddEvenResult = ({count}) => {
    return <>{count%2===0? "짝수" : "홀수"}</>    
};

export default OddEvenResult;


//Counter.js
import React,{useState} from "react";
import OddEvenResult from "./OddEvenResult";

const Counter=(props)=>{
    const [count,setCount] = useState(props.initialValue)

    const onIncrease = () => {
        setCount(count+1);
    };
    const onDecrease = () => {
        setCount(count-1);
    };
    return(
        <div>
            <h2>{count}</h2> 
            <button  onClick={onIncrease}>+</button>
            <button onClick={onDecrease}>-</button>
            <OddEvenResult count={count}/>
        </div>
    );
};

export default Counter;

OddEvenResult.js에 state와 prop을 이용해서 동적인 데이터를 계속 바꿔가면서 props를 전달할 수 있다.

OddEvenResult컴포넌트가 counter의 state가 바뀔때마다 계속 다른값을(짝수/홀수)를 렌더링하는 것을 볼 수있는데, 이렇게 리액트의 컴포넌트는 부모가내려주는 props가 변경이되면 다시렌더를 하게된다.그래서 리랜더가 일어난다.

만약에 {count} props를 받지않고, 혼자 렌더링을하는 자식요소라고 할 때에도

//OddEvenResult.js

const OddEvenResult = () => {
	console.log("render");
    return <>{10%2===0? "짝수" : "홀수"}</>    
};

export default OddEvenResult;

부모요소가 변경이되면(=counter컴포넌트의 state가 바뀌게되면) 자식요소도(=OddEvenResult컴포넌트도) 계속 리랜더가 되는것을 확인할 수 있다.

🌟렌더링을 실행하는 경우

  1. 리액트의 컴포넌트는 본인이 관리하고 본인이 가지는 state가 바뀔때
  2. 나에게 내려오는 props가 바뀔때
  3. 내 부모컴포넌트가 랜더링 되면 자식요소도 리랜더가 된다.

props로는 뭐든지 다 전달할 수 있기때문에 컴포넌트자체도 다른컴포넌트의 props로 전달할 수 있다.

//Container.js

const Container=({children})=>{
    return (
        <div style={{margin: 20, padding: 20, border: "1px solid gray"}}>
            {children}
        </div>
    );
};

export default Container;

//App.js
import React from "react";
import Container from "./container";
import Counter from "./counter";


const App = () => {
  return (
    <Container>
      <div>
        <Counter/>
      </div>
    </Container>
  );
}

export default App;

<Container>컴포넌트 사이에 JSX요소(html)을 자식으로 배치하게되면 Container컴포넌트에 {children}이라는 prop으로 전달되게된다.
그래서 {children}에 JSX요소들(<div><Counter/></div>)이 전달이 되었고 값처럼 활용을 할 수 있다.


profile
FrontEnd Developer (with 구글신)

0개의 댓글