+ 알아야 할것
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).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
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)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
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> ) } |