[React] State

vanLan·2022년 11월 16일
0

React

목록 보기
4/11
post-thumbnail

🔑 state란?

  • state는 컴포넌트에 대한 데이터 또는 정보를 포함하는데 쓰이는 React 내장 객체이다.
  • 컴포넌트 state는 시간이 지남에 따라 변경될 수 있고, 변경 될 때마다 컴포넌트가 re-rendering 되야 한다.
  • state 변경은 사용자 작업 또는 시스템 생성 이벤트에 대한 응답으로 발생할 수 있으며, 이 변경은 컴포넌트의 동작, 렌더링 방법을 결정한다.

💡 props와 state 공통점 및 차이점

  • 차이점

    propsstate
    부모 컴포넌트가 자식 컴포넌트에게 전달하는 값자신(컴포넌트)이 스스로 관리하는 상태 값
    값을 자신(자식 컴포넌트)이 변경할 수 없음값을 자신이 변경할 수 있음
  • 공통점

    props를 통해 값을 내려 받거나, 자신이 관리하고 있는 state가 변경되면 컴포넌트 렌더링이 발생한다

🗝 state가 필요한 이유

  • 함수 컴포넌트는 함수이기 때문에 호출이 일어나기 전까진 내부 값이 변경되어도 re-rendering 되지 않는다.
    이러한 문제를 해결하기 위해서 함수 컴포넌트 내부적으로 상태 값을 관리하는 일이 필요하다.

  • 상태 값을 관리 한다는 것은, 함수 내부적으로 값의 변동이 있을 때, 이를 인지하고 렌더링이 일어날 수 있다는 것을 의미한다.

  • React의 여러 Hook 중 useState가 그 역할을 한다.

    
    import React, { useState } from 'react';
    
    // state: 값
    // setState: state를 변경 시킬 때 사용되는 함수(컴포넌트의 re-rendering을 발생 시킨다.)
    const [state, setState] = useState();

🗝 불변성(immutable) - '변하지 않는 성질'

  • 프로그래밍에서 불변성을 지킨다는 것은 메모리 영역의 값을 직접적으로 변경하지 않는 것을 의미한다.
  • React는 기존 state값과 setState 실행 이후 state값을 비교하여, 값이 다르면 re-rendering을 하고 같으면 유지한다.
  • 불변성을 지키지 않고, 메모리 영역의 값을 직접적으로 변경하면 React는 state값이 변동이 있다고 인지하지 못하고 re-rendering이 발생하지 않는다.

🗝 얕은 비교(Shallow Compare)

  • React는 기존 state값과 setState 실행 이후 state값을 비교할 때, 얕은 비교(Shallow Compare)을 한다.
  • 위의 이유로 데이터 타입에 따라 setState의 사용 방법이 다르다.

🗝 state 다루기

  • 원시타입(Reference Type)

    • setState()로 state값에 바로 접근.

      
      const [number, setNumber] = useState(0);
      const [boolean, setBoolean] = useState(true);
      const [string, setString] = useState('');
      
      setNumber(number + 1);
      setBoolean(!boolean);
      setString(string + 'a');
  • 참조 타입(Primtive Type)

    • Spread Operator 이용.

    • React는 state값과 setState 실행 이후 state값을 비교할 때 얕은 비교를 하기 때문에, 객체 자체를 복사하여 새로운 주소 값에 저장.

      
      // 객체
      const [info, setInfo] = useState({
        operator: '+',
        count: 0,
        show: true
      });
      setInfo({
        ...info,
        operator: '*',
        count: info.count+1,
        show: !info.show
      });
      
      // 배열(자바스크립트에선 배열도 객체)
      const [array, setArray] = useState([]);
      setArray([...array, newItems]);
      setArray(array.filter(arr => {}));
  • 💡 비동기로 동작하는 setState()

    • state 업데이트는 비동기적일 수도 있다.

    • React는 성능을 위해 여러 setState() 호출을 단일 업데이트로 한꺼번에 처리할 경우도 있다.

    • 단일 처리시, 객체 형태로 병합한다.

      
      const add = () => setNumber(number + 1);
      const multiplyBy2 = () => setNumber(number * 2);
      const multiplyBy2AndAddBy1 = () => {
        multiplyBy2();
        add();
      };
      
      // 합병원리: number키에 마지막으로 입력된 'number + 1'이 덮어 씌워짐.
      Object.assign({number, number: number * 2, number: number + 1});
    • setState()를 연속적으로 사용해야 할 경우, 함수 형태로 입력 후 사용.

      
      // 함수 형태로 입력
      const add = () => setNumber((number) => number + 1);
      const multiplyBy2 = () => setNumber((number) => number * 2);
      const multiplyBy2AndAddBy1 = () => {
        multiplyBy2();
        add();
      };
  • 💡 React의 데이터는 아래로 흐른다

    • 하향식(top-down) 또는 단 방향식 데이터 흐름이라고도 한다.
    • 모든 state는 특정 컴포넌트가 소유하고 있고, 그 state로 부터 파생된 데이터 또는 UI 트리구조에서 자신의 하위 컴포넌트들에만 영향을 미칠 수 있다.

🗝 state 끌어올리기

  • 단일 진실 공급원(Single source of truth): 모든 데이터 요소를 한 곳에서만 제어 또는 편집하도록 조직하는 관례.

  • 하위 컴포넌트에서 각자 관리되고 있는 state를 상위 컴포넌트로 올리고 상태값을 변경할 수 있는 함수를 props로 보내주는 형태.

  • state 끌어올리기 예시

    
    // Convert.js
    import React from "react";
    import AgeInput from "./AgeInput";
    
    export default function Convertor() {
    
      return (
          <div>
              <AgeInput relation={'s'} />
              <AgeInput relation={'m'} />
          </div>
      );
    }
    
    // AgeInput.js
    import React, { useState } from "react";
    
    export default function AgeInput(props) {
      const [age, setAge] = useState(0);
      const relationNames = {
          's': `Sister's`,
          'm': 'My',
      };
      const relation = relationNames[props.relation];
      
      function handleChange(e) {
          setAge(e.target.value);
      }
      
      return (
        <fieldset>
          <legend>{`Enter ${relation} age`}</legend>
          <input value={age} onChange={handleChange} />
          <p>{props.relation === 's' ? 'My ' : `Sister's `}Age is {props.relation === 's' ? parseInt(age) - 2 : parseInt(age) + 2}</p>
        </fieldset>
      );
    }

    🎇 state 끌어 올리기 후, 상위 컴포넌트의 state 상태를 props로 전달 받아 구성할 수도 있다.
    
    // Convert.js
    import React, { useState } from "react";
    import AgeInput from "./AgeInput";
    
    export default function Convertor() {
      const [state, setState] = useState({
        relation: 'm',
        age: 0,
      });
      const handleAgeChange = (obj) => {
        setState({...state, relation: obj.relation, age: obj.age});
      }
    
      function toSisterAge(myAge) {
        return myAge + 2;
      }
    
      function toMyAge(sisterAge) {
        return sisterAge - 2;
      }
    
      function tryConvert(age, convert) {
        const input = parseInt(age);
        if(Number.isNaN(input)) return '';
    
        const output = convert(input);
        return output.toString();
      }
    
      const { relation, age } = state;
    
      const myAge = relation === 's' ? tryConvert(age, toMyAge) : age;
      const sisterAge = relation === 'm' ? tryConvert(age, toSisterAge) : age;
    
      return (
        <fieldset>
          <legend>Family age</legend>
          <AgeInput relation={'s'} age={sisterAge} onAgeChange={handleAgeChange} />
          <AgeInput relation={'m'} age={myAge} onAgeChange={handleAgeChange} />
        </fieldset>
      );
    }
    
    // AgeInput.js
    import React from "react";
    
    export default function AgeInput(props) {
      const relationNames = {
        's': `Sister's`,
        'm': 'My',
      };
      
      function handleChange(e) {
        props.onAgeChange({
            age: e.target.value,
            relation: props.relation,
        });
      }
    
      const relation = relationNames[props.relation];
      
      return (
        <div>
          <label for='input'>{relation} age is : </label>
          <input className='input' value={props.age} onChange={handleChange} />
        </div>
      );
    }

profile
프론트엔드 개발자를 꿈꾸는 이

0개의 댓글