본문 바로가기
React

[React] State - 동적 상태 관리 / 생명주기 / 데이터 전달

by 파프리카_ 2023. 7. 20.
728x90
반응형

02_State

state?

  • 일반적으로 props와 같은 javascript 객체

  • props와의 차이점

    • props : (함수 매개변수처럼) 컴포넌트에 전달

    • state : (함수 내에 선언된 변수처럼) 컴포넌트 안에서 관리

  • React로 개발된 소스 코드 내에서 데이터 교환 및 이벤트 등을 동적으로 처리해줄 수 있도록 해줌

Event 생성

예제
const Component = (props) => {

    // btnVal : 화면에 표시되는 button의 라벨명 
    let btnVal = props.btnVal; // 기존 props.btnVal에 할당된 값이 'Before-Changing'이라 가정

    // 버튼에서 onClick시 호출될 함수 정의
    const clickHandler = () => {
        console.log('Click!!!!');
        btnVal = 'Changed';
    }

    return (
        <div>
            // 아래와 같이 button 태그 생성 후에
            // `onClick` 과 같이 행위를 지정하고 그 뒤에 실행되는 작업을 {}<- javascript 코드에 할당할 수 있
            <button onClick={clickHandler}>{btnVal}</button>
        </div>
    )
}
  • 위 소스로 실행 시, 기대되는 플로우

    • 기존에 Before-Changing 이라 생긴 버튼 클릭

      1. 콘솔에 Click!!! 가 찍힘

      2. 버튼의 라벨이 Changed 로 변경됨

  • 실제 수행되는 플로우

    • 기존에 Before-Changing 이라 생긴 버튼 클릭

      1. 콘솔에 Click!!! 가 찍힘

      2. 버튼의 라벨이 Changed 로 변경되지 않음

👀 변경되지 않는 이유
  • React는 처음에 index.js파일에서 아래의 코드로 인해 App 컴포넌트 호출

    const root = ReactDOM.createRoot(document.getElementById('root'));
    
    root.render(<App />);
  • App.js 컴퍼넌트에서 컴퍼넌트 Tree 끝까지 쭉~~ 호출해서 들어가서 더이상 컴퍼넌트를 호출하지 않는 하위 트리의 컴퍼넌트까지 읽음

  • 다 읽어온 후 index.js 에서 root.render(<App />); 렌더링을 실행

  • => React는 원래 위처럼 한번의 렌더링만 수행하기 때문에 컴퍼넌트 내부에 정의된 함수는 처음 한번만 호출될 뿐 그 후에 수행한다해도 수행된 함수가 페이지에 적용되지 않음

State 관리 라이브러리 useState

  • 호출 방법 - Component.js 상단에 아래와 같이 호출

  • import React, {useState} from 'react';
  • 컴포넌트 적용 예

    import React, {useState} from 'react';
    
    const Component = (props) => {
    
        // useState를 선언
        // btnVal = 현재 상태의 값
        // setBtnVal = 값을 변경할 때 호출하는 함수
        const [btnVal, setBtnVal] = useState(props.btnVal);
    
        // 버튼에서 onClick시 호출될 함수 정의
        const clickHandler = () => {
            setBtnVal('Changed'); // 변경 
            console.log(btnVal);
        }
    
        return (
            <div>
                // 아래와 같이 button 태그 생성 후에
                // `onClick` 과 같이 행위를 지정하고 그 뒤에 실행되는 작업을 {}<- javascript 코드에 할당할 수 있
                // 유의 - 여기서 clickHandler 를 직접 호출하는 것이 아닌
                // 위에 정의된 clickHandler를 가리키는 것
                <button onClick={clickHandler}>{btnVal}</button>
            </div>
        )
    }
  • 위 소스 적용 시 진행 사항

    • -> Before-Changing 이라 생긴 버튼 클릭

      1. 버튼이 Changed 로 변경됨

      2. 콘솔에 기존 버튼명인 Before-Changing 가 찍힘

  • useState가 선언된 Component는 각 각 독립적인 상태를 가진다

    • -> Component.js가 상위 컴포넌트에서 여러 번 호출되었을 경우, 호출된 Component.js에서의 각 btnVal의 값은 저마다의 독립적인 상태를 유지한다.
    • <- why? 상위 컴포넌트에서 호출된 Component.js는 호출 회수만큼 반복되어 호출되기 때문에 그 안에서의 useState로 새로 리로딩되는 값들도 개별적인 값을 가진다.
    • <- useState 함수는 하나의 Component에서 필요에 따라 여러 번 각각 상태 변경을 원하는 변수마다 호출할 수 있다.
  • useState 함수를 const로 선언하는 이유

    • useState를 통해 반환되는 값이 재할당 되는 것이 아닌, 이벤트로 인해 useState를 통해 호출되는 함수가 새로 재평가되는 것이기 때문에 매번 새로 수행되어 고정된 값을 갖는다고 볼 수 있다. (상태값이 update되는것 X, 새로운 상태가 생성됨)

UseState 여러 개 쓰기

  • 방법 1 -> 각 각 나열

    import React, {useState} from 'react';
    
    const Component = (props) => {
    
        const [enteredTitle, setEnteredTitle] = useState('');
        const titleChangeHandler = (event) => {
            setEnteredTitle(event.target.value); 
        };
    
        const [enteredAmount, setEnteredAmount] = useState('');
        const amountChangeHandler = (event) => {
            setEnteredAmount(event.target.value); 
        };
    
        return (
            <div>
                <label>Title</label>
                <input type="text" onChange={titleChangeHandler} />
            </div>
            <div>
                <label>Amount</label>
                <input type="number" onChange={amountChangeHandler} />
            </div>
        )
    }
  • 방법 2 -> 한 번의 useState 호출해서 세트로 관리

    import React, {useState} from 'react';
    
    const Component = (props) => {
    
        const [userInput, setUserInput] = useState({
            enteredTitle : '',
            enteredAmount : ''
        });
    
        const titleChangeHandler = (event) => {
            setUserInput({
                ...userInput, 
                enteredTitle : event.target.value
            });
        };
    
        const amountChangeHandler= () => {
            setUserInput({
                ...userInput, 
                enteredAmount : event.target.value
            });
        }
    
        return (
            <div>
                <label>Title</label>
                <input type="text" onChange={titleChangeHandler} />
            </div>
            <div>
                <label>Amount</label>
                <input type="number" onChange={amountChangeHandler} />
            </div>
        )
    
    }
    • ...userInput : useState 내부에 선언된 다른 값들을 set로 움직이게 하기 위해 복사하는 것 -> ... : spread 연산자
  • 방법 3 -> 방법 2에서 변형된 방식으로 함수 선언 방식

        const titleChangeHandler = (event) => {
            setUserInput((prevState) => {
              return { ...prevState, enteredTitle: event.target.value };
            });
        };
    • 만약 상태 업데이트가 이전 상태에 의존하고 있다면, 방법3이 안전

    • prevState가 가장 최근의 스냅샷이 보장된 방법이기 때문에

상향식 데이터 전달

  • 실행 순서 (상단 컴포넌트부터 하위 컴포넌트를 따라 쭉 내려간 뒤에, 데이터를 받아 위로 쭉쭉 올린다)

    1. <App/> 실행

      1. 태그 -> <NewValues/> 선언 - props에 onAddValue 정의
    2. <NewValues/> 실행

      1. 태그 -> <ValueForm/> 선언 - props에 onSaveValueData 정의
    3. <ValueForm/> 실행

      1. useState를 통해 값 변경 시, enteredValue 에 값 생성

      2. submit 시, submitHandler 수행

      3. 다시 위로 데이터 올림

      4. props.onSaveValueData 에 입력받은 enteredValue 담아서 위로 전송

    4. <NewValues/>

      1. saveValueDataHandler 수행됨 -> props.onAddValue에 입력받은 enteredValue 담아서 위로 전송
    5. <App/>

      1. addValueHandler 수행됨 -> enteredValue 데이터 받기 성공
728x90
반응형

'React' 카테고리의 다른 글

[React] Redux (+Redux Toolkit)  (0) 2023.07.23
[React] Start React  (0) 2023.07.20