+ 알아야 할것
useState 훅과 useEffect 훅
+ 실습
App.jsx class component를 function 컴포넌트로 바꾸고 hook 적용
Player.jsx class component를 function 컴포넌트로 바꾸고 hook 적용
App.jsx 훅 적용
먼저 class component를 function 컴포넌트로 바꾸고 차례대로 적용한다.
class 의 state 선언을 useState 훅으로 전환한다(L2)
componentDidMount 부분을 useEffect 훅으로 전환한다(L4 ~ L11) this.setState 는 setPlayers로 변경(L9).
useState, useEffect 이 두가지 훅은 class 컴포넌트에서 function 컴포넌트로 변경시 필수로 사용해야 하는 훅이다. 나머지 훅들은 주로 성능 개선의 이유로 주로 사용되지만 이 두가지 훅은 반드시 변경해야 한다.
클래스 컴포넌트에서 사용된 함수는 모두 함수 표현식으로 변경한다(L13, L24, L 49). 또한 함수 안에 사용된 this.setState는 모두 setPlayers 함수로 변경한다(L19, L33, L45)
render 함수는 단순히 리턴구문으로 변경한다(L56 ~ L70). this.state 가 사용된 부분은 모두 제거한다(L58). 그리고 함수 호출에서 사용된 this 도 모두 제거한다(L64, L65, L66, L68).
const App = (props) => {
const [players, setPlayers] = useState([]);
useEffect(() => {
axios.get('http://api.eastflag.co.kr:8000/api/score/list')
.then(response => {
console.log(response);
const {data} = response;
setPlayers(data);
});
}, [])
const handleRemovePlayer = (id) => {
axios.delete(`http://api.eastflag.co.kr:8000/api/score?id=${id}`)
.then(response => {
console.log(response);
const {data} = response;
if (data.result === 0) {
setPlayers(players.filter(item => item.id !== id));
}
});
}
const handleChangeScore = (id, delta) => {
console.log('id: ' + id, 'delta: ' + delta);
const copyPlayers = [ ...players ];
copyPlayers.forEach(player => {
if (player.id === id) {
player.score += delta;
}
})
setPlayers(copyPlayers);
}
const handleAddPlayer = (name) => {
console.log(name);
axios.post('http://api.eastflag.co.kr:8000/api/score', {name})
.then(response => {
console.log(response);
const {data} = response;
const copyPlayers = [ ... players ];
copyPlayers.unshift(data);
setPlayers(copyPlayers);
});
};
const getHighScore = () => {
const maxObject = _.maxBy(players, 'score');
const highScore = maxObject.score;
// 0은 디폴트이므로 0보다 클 경우만 highScore로 지정한다.
return highScore > 0 ? highScore : null;
}
return (
<div className="container p-3">
<Header title="My scoreboard" players={players} />
{/*Players List*/}
{ players.map(item =>
<CustomPlayer key={item.id}
name={item.name} score={item.score} id={item.id}
isHighScore={item.score === getHighScore()}
removePlayer={handleRemovePlayer}
changeScore={handleChangeScore} />) }
<AddPlayerForm addPlayer={handleAddPlayer}></AddPlayerForm>
</div>
)
}
AddPlayerForm.jsx 훅으로 변경
생성자 함수안에 createREf()는 useRef()로 변경(L2, L3).
state 변수는 useState 훅으로 변경(L4)
함수는 함수 표현식으로 모두 변경(L6, L10). 함수 내부에 선언된 this는 모두 삭제한다(L7, L13 ~ L14, L24, L25)
render 함수안에 있는 return문만 가져온다(L30 ~ L49). 내부에 선언된 this는 모두 삭제한다(L32, L33, L36, L39)
export const AddPlayerForm = (props) => {
const formRef = useRef();
const textRef = useRef();
const [value, setValue] = useState('');
const handleValueChange = (e) => {
setValue(e.target.value);
};
const handleSubmit = (e) => {
e.preventDefault();
const form = formRef.current; // form node
const player = textRef.current; // input node
console.log(player.validity.valid);
console.log(form.checkValidity());
if (!form.checkValidity()) {
form.classList.add('was-validated');
return;
}
props.addPlayer(value);
setValue('')
form.classList.remove('was-validated');
}
return (
<div className="container">
<form ref={formRef} noValidate
className="row player align-items-center needs-validation" onSubmit={handleSubmit}>
<div className="col-9">
{/*<label htmlFor="playerName" className="form-label">Player Name</label>*/}
<input ref={textRef}
type="text" className="form-control" id="playerName" placeholder="input player name"
required
value={value} onChange={handleValueChange}></input>
<div className="invalid-feedback">
Please input name.
</div>
</div>
<div className="col-3 d-grid">
<button type="submit" className="btn btn-primary">Add Player</button>
</div>
</form>
</div>
)
}