state(상태)는 React의 꽃이라 볼 수 있다.
상태는 일반 JS 객체이며 렌더링 결과물에 영향을 주는 정보를 가지고 있다.
상태가 변경된다면 자동으로 rerendering이 되는데 이것을 통해 앱의 데이터를 동적으로 다룰 수 있다.
따라서 우리는 상태를 관리하는 것에 예민해야 하며 잘 못 작성된 상태로 많은 rerendering이 발생한다면 앱의 성능을 저하시킬 수 있다.
그래서 상태를 관리하기 위한 라이브러리들도 존재한다.
이 라이브러리들은 컴포넌트들간의 상태를 공유 및 관리가 가능하게 만들어 준다.
component 안에서 변경(관리)이 가능한 데이터
🔔 UI를 새로고침해 주는 코드가 존재하지 않기 때문에 버튼을 클릭해도 페이지가 rerendering 되기 전까진 바뀐 count는 업데이트가 안된다.
import { useState } from 'react';
function Example() {
const [count, setCount] = useState(0);
return (
<div>
<p>버튼을 {count}번 눌렀습니다.</p>
<button onClick={() => setCount(count + 1)}>클릭</button>
</div>
);
}
🔔 setState
상태 값을 바꾸는데 setState를 사용한다.
이 훅은 상태값이 바뀌면 rerendering을 해야하는 타이밍이라는 걸 알게 해준다.
만약 사용하지 않고 그냥 state를 바꾼다면 rerendering이 일어나지 않아 바뀐 데이터가 포함된 UI가 표현되지 않을 것이다.
setState는 이벤트 핸들러 내에서 비동기적으로 작동한다.
만약 동기적으로 작동하게 만들고 싶다면 useEffect를 사용할 수 있다.
setState를 사용하는데는 두가지 방법이 있다.
const [count, setCount] = useState(0)
// 1. 변경할 값 넣기
setCount(count+1)
// 2. 함수를 넣기
setCount(prev=> prev+1)
1번은 상태 값이 어디서 어떤식으로 변경이 된 후 여기 있는지 모를 경우가 있는데 그 경우 의도하는 상태 값이 아닐 수 도 있다.
만약 여기 저기 상태의 set을 많이 하는 경우 이 방법은 지양해야한다.
2번은 함수를 넣어 이전 상태값을 기준으로 변경하기 때문에 좀 더 안전한 방법이다.
🔔 분을 시간으로 시간을 분으로 바꾸는 기능을 가진 App
🔧 순서
onChange는 변화를 감지하고 value는 값을 받는다.
flipped의 초기값은 false로 설정했고 함수가 실행된다면 flipped 초기값의 반대인 !flipped로 된다.
setFlipped((current)=>!current);
inverted가 true일때 disabled가 활성화 된다.
function App() {
const [amount,setAmount] = useState();
const [inverted,setInverted]= useState(false);
const onChange = (e) =>{
// 이벤트의 값을 minutes에 넣는다.
setAmount(e.target.value);
}
const reset = () => setAmount(0);
const onFlip = () => setFlipped((current)=>!current);
return(
<div>
<h3>Time Converter</h3>
<label htmlFor="min">Minutes</label>
<input value={inverted ? amount*60 : amount} id="min" placeholder="Min" type="number" onChange={onChange}disabled={inverted}/>
<h4>시간(hr)으로 바꾸기 원하는 분(min)은?{minutes}</h4>
<label htmlFor="hr">Hours</label>
<input id="hr" placeholder="Hour" type="number" value={inverted ? amount : Math.round(amount / 60)} disabled={!inverted}/>
<button onClick={reset}>Reset</button>
<button onClick={onFlip}>{inverted ? "Turn back" : "Invert"}</button>
</div>
);
};
컴포넌트를 분리시킬때(부모 > 자식으로)나 중복되는 컴포넌트를 통해 원하는 인자(데이터)들을 주고받을 경우가 필요할때 이용한다.
🔔 Btn컴포넌트를 이용할때 Btn({text:"Save"})로 인자가 들어있는 함수를 사용하는 것과 같다. props안에 인자 text와 같은 것은 임의로 설정 가능하다.
ex)
function Btn(props){
console.log(props); // {text: "Save",x:false}, {text:"Continue",y:7}
return <button onClick={props.changeValue}>{props.text}</button>;
}
const MemorizedBtn = React.memo(Btn);
function App(){
const [value,setValue] = useState("Save");
const changeValue = () => setValue("Changed");
return(
<div>
<MemorizedBtn text="Save" x={false} changeValue={changeValue}/>
<MemorizedBtn text="Continue" y=7/>
</div>;
)
}
text="Save"와 banana="Save"는 컴포넌트에서 받는 값만 서로 같다면 역할을 할 수 있다.
함수도 props를 통해 내릴 수 있다. 이 때 내릴 함수는 부모에서만 관리가 가능하다.
function Btn({text}){
return <div>{text}</div>;
}
🔔 const MemorizedBtn = React.memo(Btn);
부모 컴포넌트 state변경이 존재한다면 모든 자식들은 rerender된다.
하지만 React.memo의 기능은 state가 변경된 자식만 rerender된다.
PropTypes는 props의 형태를 실수하지 않게 도와준다. jsDoc과 같은 역할을 한다.
Btn.propTypes = {
text : PropTypes.string,
};
State는 자기 자신 컴포넌트 내에서 바꿀 수 있는 값
Props는 부모에서 자식에게 내리는 값 컴포넌트는 이 값을 변경할 수 없다.
부모로부터 받은 props 와 state 초기값은 모두 Component 내부에 정의된 기본값보다 우선이다.
공통점
차이점
상위 컴포넌트로 변경이 가능한가?
state : no <> props : yes
컴포넌트 내부에서 변경이 가능한가?
state : yes <> props : no
자식 구성 요소에서 변경이 가능한가?
state : no <> props : yes