React.JS 자습서를 타입스크립트로.. (3)
- 삽질 정보/Javascript, Typescript
- 2022. 6. 27.
1. State를 Square 각각에서 Board로 올리기.
(1) Board의 state에 squares 배열 추가하기.
class Board extends React.Component {
constructor(props) {
super(props);
this.state = {
squares: Array(9).fill(null),
};
}
}
먼저 이러한 형태로 만들라고 하고 있다.
Board 컴포넌트의 state에 모든 Square의 상태를 저장할 것을 요구한다.
이전에 했던 대로, Board컴포넌트의 State에 들어갈 값을 미리 인터페이스로 정의하는 과정이 필요하다.
type BoardStateElement = string | null;
type BoardState = {
squares: Array<BoardStateElement>;
};
위와 같이 SquareState 타입의 Array가 들어갈 수 있게 인터페이스 타입을 정의하였다. 자습서의 예제에 따르면 null도 들어갈 수 있으므로, null도 들어갈 수 있는 타입으로 정의해준다.
마찬가지로, Board 또한 생성자를 넣어준다.
class Board extends React.Component<any, BoardState> {
constructor(props: BoardState) {
super(props);
this.state = {
squares: Array<BoardStateElement>(9).fill(null)
}
this.state.squares[0] = 'X';
this.state.squares[1] = 'O';
this.state.squares[5] = '?';
}
}
정상적으로 동작하는 것을 확인하기 위해, 0, 1, 5번 인덱스에 각각 'X', 'O', '?' 값을 대입해둔다.
※ Board의 컴포넌트의 제네릭의 첫번째 인자로 any를 지정했다. Board에는 별도로 props를 받는 부분이 없으므로, 생략하기 위해서 그렇게 하였다. 대신 State는 BoardState 타입을 사용하기 때문에 두 번째 제네릭 인자로 BoardState가 오는 것은 필수적이다.
(2) Square에서 필요 없어진 부분 삭제 및 Props 정리
더 이상 Square에서 state를 관리할 필요가 없다. 따라서 state를 설정하는 생성자를 모두 삭제해도 상관이 없다.
또한, 클릭 시 state를 변경하는 코드도 필요가 없어졌다. 우리는 이제 단순히 상위 컴포넌트인 Board에서 props만 받아오면 된다.
type SquareProps = {
value: BoardStateElement;
};
class Square extends React.Component<SquareProps> {
render() {
return (
<button className="square">
{ this.props.value }
</button>
);
}
}
필요 없는 부분을 모두 쳐내고, 위와 같이 가장 최소한의 Square 코드만이 남았다.
예상대로 잘 출력됨을 확인할 수 있다.
(3) Square의 value에 'O', 'X', null 만 허용하기
타입스크립트의 타입 정의를 활용한다.
지금은 type BoardStateElement = string | null; 형태로 되어 있는 부분을 수정한다.
방법은, string을 'O', 'X' 만 받을 수 있게 고친다.
다음과 같이 된다.
type BoardStateElement = 'O' | 'X' | null;
수정 직후, 아까 Board의 생성자에서 squares에 값을 대입했던 곳에서 오류가 나온다.
ERROR in src/index.tsx:32:9
TS2322: Type '"?"' is not assignable to type 'BoardStateElement'.
30 | this.state.squares[0] = 'X';
31 | this.state.squares[1] = 'O';
> 32 | this.state.squares[5] = '?';
| ^^^^^^^^^^^^^^^^^^^^^
33 | }
34 |
35 | renderSquare(i: number) {
'?'를 대입할 수 없다는 코드이다. 예상한대로 동작하고 있다!
2. 각 사각형에 클릭 이벤트 추가하기
Board의 renderSquare함수를 아래와 같이 수정.
renderSquare(i) {
return (
<Square
value={this.state.squares[i]}
onClick={() => this.handleClick(i)}
/>
);
}
Square 컴포넌트를 아래와 같이 수정.
class Square extends React.Component {
render() {
return (
<button
className="square"
onClick={() => this.props.onClick()}
>
{this.props.value}
</button>
);
}
}
(1) 클릭 이벤트 만들기
먼저, Board 컴포넌트에 클릭을 처리할 handleClick 함수를 만든다.
handleClick(i: number) {
const states = this.state;
states.squares[i] = 'X';
this.setState(states);
}
클릭 시 해당 위치의 state가 'X'로 변경되는 간단한 함수이다.
이후, 이 handleClick 함수를 Square에 props를 통해 전달해주는 부분을 수정한다. Board에서 넘기는 부분은 문제가 없으나, SquareProps에 onClick 속성이 정의되지 않았으므로 오류가 생긴다.
type SquareProps = {
value: BoardStateElement;
onClick: () => void;
};
아래와 같이, void형 함수임을 나타내는 타입과 함께 onClick 속성을 추가로 정의한다.
나머지는 자습서에 나온 것 이외에는 수정할 부분이 별도로 없는 것 같다.
실행 결과, 예상한 대로 정상적으로 잘 된다.
3. 오매 불변성...
handleClick 함수가 없어서, 임의로 만들었다. 그런데 변수 대입 시 당연히 복사가 일어날 줄 알았는데, 아니었나보다...
handleClick(i) {
const squares = this.state.squares.slice();
squares[i] = 'X';
this.setState({squares: squares});
}
자습서에는 이렇게 slice() 함수를 사용해서 배열을 복제할 것을 추천하고 있다.
여러가지 장점이 있는데, 리액트의 순수 컴포넌트를 만드는 것에 활용이 된다는 것을 눈여겨 볼 만한 것 같다.
이 부분은 나중에 더 찾아봐야겠다.
아무튼 따라서, 내 handleClick 함수도 변경되었다.
handleClick(i: number) {
const squares = this.state.squares.slice();
squares[i] = 'X';
this.setState({squares: squares});
}
4. 함수 컴포넌트
함수 컴포넌트는 단순하게 render만 갖는 컴포넌트 클래스를, 단순화 시킬 수 있는 방법.
자습서 코드에서 타입 형태만 인자(props: SquareProps)에 추가하는 것으로 더 이상 수정할 것이 없다.
function Square(props: SquareProps) {
return (
<button
className="square"
onClick={() => props.onClick()}
>
{ props.value }
</button>
);
}
'삽질 정보 > Javascript, Typescript' 카테고리의 다른 글
React.JS 자습서를 타입스크립트로.. (2) (0) | 2022.06.27 |
---|---|
React.JS 자습서를 타입스크립트로.. (1) (0) | 2022.06.27 |
Javascript Var, Let, Const (0) | 2021.12.11 |