[리액트] Lifting State Up

이지민·2025년 6월 2일

리액트

목록 보기
14/15

1. Shared State ⭐⭐⭐

  • 공유된 state

  • 자식 컴포넌트들이 가장 가까운 공통된 부모 컴포넌트의 state를 공유하여 사용하는 것
    (== 하위 컴포넌트가 공통된 상위 컴포넌트의 state를 공유하여 사용하는 것)

  • 특정 컴포넌트의 state에 있는 데이터를 여러 개의 하위 컴포넌트에서 공통적으로 사용하는 경우


  • 왼쪽 그림의 경우, 자식 컴포넌트가 각각 값을 가지고 있을 필요 없이 부모 컴포넌트의 state에 있는 값에 연산(x2, x3…)을 해주면 된다.

  • 오른쪽 그림의 경우도, 자식 컴포넌트가 각각 온도값을 가지고 있을 필요 없이 부모 컴포넌트의 온도값을 변환해주면 된다.



2. 하위 컴포넌트에서 State 공유하기

섭씨 온도를 props로 받아서 물의 끓음 여부를 문자열로 출력해주는 컴포넌트 만들기

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

이 컴포넌트를 사용하는 부모 컴포넌트를 만들기

function Calculator(props){
	const [temperature, setTemperature] = useState('');
	
	const handleChangeTemp = (event) => {
		setTemperature(event.target.value);
	}
	
	return (
		<fieldset>
			<legend>섭씨 온도를 입력하세요:</legend>
			<input value = {temperature} onChange = {handleChangeTemp}/>
			<BoilingVerdict
				celsius = {parseFloat(temperature)} />
		</fieldset>
	)
}

Calculator 컴포넌트 안에 온도를 입력하는 부분을 별도로 추출하기

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 = {setTemperature}/>
		</fieldset>
	);
}

Calculator 컴포넌트 변경하기

function Calculator(props){
	return(
		<div>
			<TemperatureInput scale = "c" />
			<TemperatureInput scale = "f" />
		</div>
	);
}
  • ⚠️ 사용자가 입력하는 온도값이 TemperatureInput의 state에 저장되기 때문에 섭씨온도와 화씨온도 값을 따로 입력받으면 2개의 값이 다를 수 있음.

  • 2개의 TemperatureInput은 독립된 element이므로, 입력값이 각각의 state에 저장됨값을 동기화시켜줘야 함.


온도 변환 함수 작성하기

function toCelsius(f){
	return (f - 32) * 5 / 9;
}

function toFahrenheit(c){
	return (c * 9 / 5) + 32;
}

function tryConvert(temperature, convert){
	// parameter: (온도 값, 변환하는 함수)
	const input = parseFloat(temperature);
	
	if(Number.isNaN(input)){ // exception 처리
		return '';
	}
	
	const output = convert(input);
	const rounded = Math.round(output * 1000)/ 1000;
	return rounded.toString();
}
tryConvert('abc', toCelsius); // empty string 리턴
tryConvert('10.22', toFahrenheit); // '50.396'을 리턴

Shared State 적용하기 ⭐⭐

  • ✅lifting state up : 하위 컴포넌트의 state를 공통된 상위 컴포넌트의 state로 끌어올리는 것

  • 일반적인 props의 전달 방식은 상위 → 하위의 흐름

    • 부모가 데이터를 가지고 있고 자식은 보여주기만 함
  • lift state up의 경우는 하위 → 상위 → 하위의 흐름

    • 자식이 입력을 바꾸고 그것을 부모에게 함수로 알려줌
    • 부모가 상태를 바꾸고 다시 자식들에게 props로 전달

  • TemperatureInput에서 온도값을 가져오는 부분 수정하기

    function TemperatureInput(props){
    ...
    
    return (
    ...
    // 변경 전: <input value = {temperature} onChange = {setTemperature}/>
    <input value = {props.temperature} onChange = {handleChange} />
    ...
    );
    }
    • 온도 값을 컴포넌트의 state에서 가져오는 것이 아닌, props를 통해 가져온다.

    • 컴포넌트의 state를 사용하지 않게 되기 때문에, 입력값이 변경되었을 때 상위 컴포넌트로 변경된 값을 전달해주어야 함.


  • handleChange 함수 변경하기
    const handleChange = (event) => {
    // 변경 전: setTemperature(event.target.value);
    props.onTemperatureChange(event.target.value);
    }
    • 사용자가 온도값을 변경할 때 마다 props의 onTemperatureChange 함수를 통해 변경된 온도 값이 상위 컴포넌트로 전달됨.

  • 최종적으로 완성된 TemperatureInput 컴포넌트

    function TemperatureInput(props) {
    // console.log(props);
    
    // 호출 순서 2. : 상위 컴포넌트의 상태 변경 함수(props로 전달된 함수)를 호출
    const handleChange = (event) => {
    props.onTemperatureChange(event.target.value);
    };
    
    return (
    <fieldset>
      <legend>온도를 입력해주세요. 단위: {scaleType[props.scale]}</legend>
      {/* 호출 순서 1 : onChange되면 handleChange 호출*/}
      <input type="text" value={props.temperature} onChange={handleChange} />
    </fieldset>
    );
    }

Shared State를 고려하여 Caculator 컴포넌트 변경하기

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

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

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

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

  return (
    <div>
      {/* 호출 순서 3. :  하위 컴포넌트로부터 props.onTemperature의 호출을 받고 특정한 이벤트 핸들러 실행*/}
      <TemperatureInput scale="c" temperature={celsius} onTemperatureChange={handleCelsiusChange} />
      <TemperatureInput scale="f" temperature={fahrenheit} onTemperatureChange={handleFahrenheitChange} />
      <BoilingVerdict celsius={parseFloat(celsius)} />
    </div>
  );
}



컴포넌트 최종구조


인용 자료 출처
처음 만난 리액트

profile
모든 것을 다 기억할 수는 없기에 기록합니다.

0개의 댓글