12. Lifting State Up - Shared State, 하위 컴포넌트에서 State 공유하기

dmalk k·2023년 9월 15일

소플의 리액트

목록 보기
32/50

# Shared State

- 하나의 데이터를 여러 컴포넌트에서 다뤄야 할때가 많다
- 부모 state를 만들어 각각의 컴포넌트에서 공유하여 사용한다


# 섭씨온도, 화씨온도 만들기

섭씨온도에서 물이 끓는지 안끊는지 코드

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

사용자로부터 온도값을 입력받는 코드

function Calculator(props) {
  const [temperature, setTemperature] = useState(''); // 스트링타입 변수 같은 역할
 
  const handleChange = (event) => {
    setTemperature(event.target.value); // onChange로 인해 변경되는 input태그의 값을 setTemperature에 대입
  }
 
  return (
    <fieldset>
      <legend>섭씨 온도를 입력하세요:</legend>
      <input
        value={temperature} // 처음에는 useState('')로 인해 공백으로 초기화 되어있다
        onChange={handleChange}/>
      <BoilingVerdict
        celsius={parseFloat(temperature)}/> // temperature는 스트링타입임으로 parseFloat으로 실수형타입으로 변경해 BoilingVerdict컴포넌트에게 주었다
	</fieldset>
  )
}

입력 컴포넌트 추출하기(재사용성, 별도로 사용)

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>
  )
}  

추출당한 Calculator컴포넌트

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

온도 변환 함수 작성하기(섭씨값 따로, 화씨값 따로인 따로국밥이 아니라, 섭씨값을 받으면 자동으로 화씨값으로 변환해 저장해두어서 값이 연결되게 하기 위함)

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)) { // isNaN : is Not a number, 숫자가 아닌 타입의 값을 입력했을 경우
    return '';
  }
  const output = convert(input); // input 안에 temperature값이 들어가 있다
  const rounded = Math.round(output * 1000) / 1000;
  return rounded.toString();
}

tryConvert컴포넌트 사용하기

tryConvert('abc', toCelsius); 		// empty string을 리턴
tryConvert('10.22', toFahrenheit);	//'50.396'을 리턴

Shared State적용하기
- Lifting state up : 하위 컴포넌트의 state를 상위 state(부모)로 올린다는 뜻
- 입력 컴포넌트 추출하기 -> 에서 return 부분을 수정하면 된다

return (
  ...
  	//<input value={temperature} onChange={handleChange}/>
  	  <input value={props.temperature} onChange={handleChange}/>
  ...
)

- 입력 컴포넌트 추출하기 -> 에서 handleChange 부분을 수정하면 된다

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

- 입력 컴포넌트 추출하기 -> 최종결과

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

function TemperatureInput(props) {
  // const [temperature, setTemperature] = useState(''); state대신 props에서 값을 전달 받는다
 
  const handleChange = (event) => {
    //setTemperature(event.target.value); 
  	props.onTemperatureChange(event.target.value); 
  }
 
  return (
    <fieldset>
      <legend>
        온도를 입력해 주세요(단위:{scaleNames[props.scale]}:
      </legend>  
      <input value={props.temperature} onChange={handleChange} />
    </fieldset>
  )
}  

Calculator 컴포넌트 변경하기

function Calculator(props) {
  const [temperature, setTemperature] = useState(''); 
  const [scale, setS  cale] = useState('c'); // 섭씨로 초기화
 
  const handleCelsiusChange = (temperature) => {
    setTemperature(temperature); // 전달받은 값으로 temperature설정
    setScale('c');// 온도 단위를 섭씨로 변경
  }
 
  const handleFahrenheitChange = (temperature) => {
    setTemperature(temperature);// 전달받은 값으로 temperature설정
    setScale('f');// 온도 단위를 섭씨로 변경
  }
 
  const celsius = scale === 'f' ? tryConvert(temperature, toCelsius):temperature; 
  // 섭씨로 들어온 값의 scale이 맞다면 tryConvert를 통해 toCelsius로 섭씨로 변경, 아니면 temperature로 그냥 전달
  const fahrenheit = scale === 'c' ? tryConvert(temperature, toFahrenheit):temperature;
  // 화씨로 들어온 값의 scale이 맞다면 tryConvert를 통해 toFahrenheit으로 화씨로 변경, 아니면 temperature로 그냥 전달
 
  return (
    <div>
      <TemperatureInput
        scale="c" 
        // 섭씨로 변경
        temperature={celsius} 
        // celsius를 통해 들어온 값이 섭씨가 맞으면 섭씨단위로 값을 변경
        // TemperatureInput컴포넌트의 input 타입의 벨류를 celsius로 변경
        onTemperatureChange={handleCelsiusChange}/>
        // handleCelsiusChange 핸들러를 통해 setTemperature(), setScale()
      <TemperatureInput
        scale="f" 
        // 화씨로 변경
        temperature={fahrenheit}
        // fahrenheit을 통해 들어온 값이 화씨가 맞으면 화씨단위로 값을 변경
        // TemperatureInput의 input 타입의 벨류를 fahrenheit 변경
        onTemperatureChange={handleFahrenheitChange}/>
        // handleFahrenheitChange 핸들러를 통해 setTemperature(), setScale()
      <BoilingVerdict
        celsius={parseFloat(celsius)}/>
    </div>
  );
}
profile
페라리 타는 백엔드 개발자

0개의 댓글