React 13. Shared State

@t189216·2024년 3월 8일

😎 프론트엔드

목록 보기
25/31

Shared State


하위 컴포넌트가 공통된 부모 컴포넌트의 state를 공유하여 사용하는 것을 말합니다.

출처

function BoilingVerdict(props) {
    if (props.celsius >= 100) {
        return <p>물이 끓습니다.</p>;
    }
    return <p>물이 끓지 않습니다.</p>;
}

BoilingVerdict 컴포넌트는 섭씨온도 값을 props로 받아서 100°C 이상이면 물이 끓는다는 문자열을 출력하고 그 외에는 물이 끓지 않는다는 문자열을 출력합니다.

function Calculator(props) {
    const [temperature, setTemperature] = useState('');

    const handleChange = (event) => {
        setTemperature(event.target.value);
    }

    return (
        <fieldset>
            <legend>섭씨 온도를 입력하세요:</legend>
            <input
                value={temperature}
                onChange={handleChange} />
                <BoiligVerdict
                    celsius={parseFloat(temperature)} />
        </fieldset>
    )
}

BoilingVerdict 컴포넌트의 부모 컴포넌트인 Calculator 컴포넌트는 state로 온도값을 하나 갖고 있습니다. 사용자로부터 입력을 받기 위해서 input 태그를 사용하여 Controlled Component 형태로 구현되어 있습니다.

사용자가 온도값을 변경할 때마다 handleChange 함수가 호출되고, setTemperature 함수를 통해 온도값을 갖고 있는 temperature라는 이름의 state를 업데이트 합니다.

state에 있는 온도값은 앞에서 만든 BoilingVerdict 컴포넌트에 celsius라는 이름의 prop으로 전달됩니다.

const scaleNames = {
    c: '섭씨',
    f: '화씨'
};

function TemperatureInput(props) {
    const [temperature, setTemperature] = useState('');

    const handleChange = (event) => {
        setTemperature(event.target.value);
    }

    return (
        <fieldset>
            <legend>
                온도를 입력해 주세요(단위:{scaleNames[props.scale]}):
            </legend>
            <input value={temperature} onChange={handleChange} />
        </fieldset>
    )
}

온도를 입력받기 위한 TemperatureInput 컴포넌트입니다. Calculator 컴포넌트에서 온도를 입력받는 부분을 추출하여 별도의 컴포넌트로 만든 것입니다. 추가적으로 props의 단위를 나타내는 scale을 추가하여 온도의 단위를 섭씨 또는 화씨로 입력 가능합니다.

추출한 컴포넌트를 사용하도록 Calculator 컴포넌트를 변경합니다.

function Calculator(props) {
    return (
        <div>
            <TemperatureInput scale="c" />
            <TemperatureInput scale="f" />
        </div>
    );
}

여기서 사용자가 입력하는 온도 값이 temperature input의 state에 저장되기 때문에 섭씨 온도와 화씨 온도 값을 따로 입력받으면 2개의 값이 다를 수 있습니다. 이를 해결하기 위해서 값을 동기화 시켜줘야 합니다.

// 화씨 온도로 변환
function toCelsius(fahrenheit) {
    return (fahrenheit - 32) * 5 / 9;
}

// 섭씨 온도로 변환
function toFahrenheit(celsius) {
    return (celsius * 9 / 5) + 32;
}
function tryConvert(temperature, convert) {
    const input = parseFloat(temperature);
    if (Number.isNaN(input)) {
        return '';
    }
    const output = convert(input);
    const rounded = Math.round(output * 1000) / 1000;
    return rounded.toString();
}

tryConvert 함수는 온도 값과 변환하는 함수를 파라미터로 받아서 값을 변환시켜 리턴합니다.

하위 컴포넌트의 state를 공통 상위 컴포넌트로 올리는 과정을 lifting state up이라고 표현합니다. 최종적으로 완성된 TemperatureInput 컴포넌트의 모습은 다음과 같습니다.

function TemperatureInput(props) {
    const handleChange = (event) => {
        props.onTemperatureChange(event.target.value);
    }

    return (
        <fieldset>
            <legend>
                온도를 입력해 주세요(단위:{scaleNames[props.scale]}):
            </legend>
            <input value={props.temperature} onChange={handleChange} />
        </fieldset>
    )
}

변경된 TemperatureInput 컴포넌트에 맞춰 Calculator 컴포넌트를 변경합니다.

import { useState } from "react";

function Calculator(props) {
    const [temperature, setTemperature] = useState('');
    const [scale, setScale] = useState('c');

    const handleCelsiusChange = (temperature) => {
        setTemperature(temperature);
        setScale('c');
    }

    const handleFahrenheitChange = (temperature) => {
        setTemperature(temperature);
        setScale('f');
    }

    const celsius = scale === 'f' ? tryConvert(temperature, toCelsius) : temperature;
    const fahrenheit = scale === 'c' ? tryConvert(temperature, toFahrenheit) : temperature;

    return (
        <div>
            <TemperatureInput
                scale="c" 
                temperature={celsius}
                onTemperatureChange={handleCelsiusChange} />
            <TemperatureInput
                scale="f"
                temperature={fahrenheit}
                onTemperatureChange={handleFahrenheitChange} />
            <BoilingVerdict
                celsius={parseFloat(celsius)} />
        </div>
    );
}
profile
Today I Learned

0개의 댓글