// 함수기반
function ProfilePage(props) {
const showMessage = () => {
alert('Followed ' + props.user);
};
const handleClick = () => {
setTimeout(showMessage, 3000);
};
return (
<button onClick={handleClick}>Follow</button>
);
}
// 클래스기반
class ProfilePage extends React.Component {
showMessage = () => {
alert('Followed ' + this.props.user);
};
handleClick = () => {
setTimeout(this.showMessage, 3000);
};
render() {
return <button onClick={this.handleClick}>Follow</button>;
}
}
언뜻보기에는 완전히 동일한 동작을 할 것 처럼 보여진다
테스트 방식
차이
'Followed Dan'
'Followed Sophie'
라고 쓰여진걸 볼 수 있다.먼저 알아야할 것 - 클래스 컴포넌트의 작동방식
showMessage
는 this.props.user
로 부터 값을 불러오는데, Props는 불변의 값(immutable)이다
하지만 this
는 변경가능하며(mutable), 이를 조작할 수 있다.
왜 클래스형 컴포넌트에서는 Sophie 로 나오는가?
여기서 생각해볼만한 관측요소 - UI 성질
하지만, 결과는 종속적이지 않다
this.props.something
이나 this.state.something
과 같은 코드를 포함해야한다면 또 다시 문제에 부딪히게 될 것이다.class ProfilePage extends React.Component {
showMessage = (user) => {
alert('Followed ' + user);
};
handleClick = () => {
// 여기서 props 에서 값을 미리 가져오고, showMessage에서 매개변수로 저장된 값을 넘겨주는 형태로 바꾸어주었다.
const {user} = this.props;
setTimeout(() => this.showMessage(user), 3000);
};
render() {
return <button onClick={this.handleClick}>Follow</button>;
}
}
// render에서 props 와 state를 클로저로 감싸주는 방법
// 이렇게 하면 클로저 안에 잇는 코드들(showMessage를 포함한)들은 render 당시의 props를 그대로 사용할 수 있다.
class ProfilePage extends React.Component {
render() {
// props의 값을 고정!
const props = this.props;
// Note: 여긴 *render 안에* 존재하는 곳이다!
// 클래스의 메서드가 아닌 render의 메서드
// this.props.user가 아닌 props.user 임을 확인하자
const showMessage = () => {
alert('Followed ' + props.user);
};
const handleClick = () => {
setTimeout(showMessage, 3000);
};
return <button onClick={handleClick}>Follow</button>;
}
}
// class 로 만들었던 것을 function 방식으로 바꿔보자
function ProfilePage({ user }) { // props를 분해한 더 명확한 표현 { user }
const showMessage = () => {
alert('Followed ' + user);
};
const handleClick = () => {
setTimeout(showMessage, 3000);
};
return (
<button onClick={handleClick}>Follow</button>
);
}
만약 함수형에서 특정 render에 종속된 것 말고 가장 최근의 state나 props를 읽고 싶다면 어떻게 해야할까?
클래스형일 경우
this.props
, this.state
를 읽어오면 되었다.함수형일 경우
ref
를 사용한다current
속성이 변경가능하고 클래스의 인스턴스 속성과 유사하게 모든 값을 보유할 수 잇는 일반 컨테이너ref를 이용해서 최신값을 유지하는 예시 - handleMessageChange로 ref를 수동변경
ref 사용 데모
ref 사용안한 것 데모
// ref 를 사용하면 class처럼 동작하게 할 수 있다.
function MessageThread() {
const [message, setMessage] = useState(''); // 여기서 message는 input value와 연동되어서 값을 저장하는데 사용한다.
const latestMessage = useRef(''); // ref 사용
// 최신값을 쫓아간다
const showMessage = () => {
alert('You said: ' + latestMessage.current);
};
const handleSendClick = () => { // button onClick에 붙여줄것
setTimeout(showMessage, 3000);
};
const handleMessageChange = (e) => { // input에 onChange에 붙여줄 것
setMessage(e.target.value);
latestMessage.current = e.target.value;
};
// 만약 ref를 안쓰고 했다면 버튼을 누르고 input값을 alert가 뜨기전에 변경한다면 변경된 값으로 alert가 뜨지 않는다.
function MessageThread() {
const [message, setMessage] = useState('');
const showMessage = () => {
alert('You said: ' + message);
};
const handleSendClick = () => {
setTimeout(showMessage, 3000);
};
const handleMessageChange = (e) => {
setMessage(e.target.value);
};
return (
<>
<input value={message} onChange={handleMessageChange} />
<button onClick={handleSendClick}>Send</button>
</>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<MessageThread />, rootElement);
ref 사용시 주의할 점
ref는 고정된 값이 아니기 때문에 랜더링 도중에 읽거나 쓰는 것을 피하는 것이 좋다.
(랜더링 내에서는 예측 가능한 일들만 일어나는 것이 권장되기 때문)
하지만, 특정 prop 과 state의 최신 값을 불러오고 싶을 때마다 ref를 수동으로 처리하는 것은 불편하니 Hook의 effect를 이용해 자동화할 수 있다.
Hook effect를 이용하여 ref 업데이트를 자동화 한 예시