+ 알아야 할것

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