Intro
In this time, I tried Hooks.
[TypeScript] Try React 1
[TypeScript] Try React + Redux
Try Hooks
Introducing Hooks – React
According to the documents, Hooks were for using states and side-effects in function components.
State
"useStates" gave me current states and functions what create next states like Reducer of Redux.
App.tsx
import 'raf/polyfill';
import React, { useState, useEffect } from 'react';
import logo from './logo.svg';
import './App.css';
import { renderBoard } from './tic-tac-toe-hooks/BoardHook';
import { BoardState } from './tic-tac-toe/Board';
function App() {
    // default value and the "state" type came from the argument of "useState". 
  const [state, setBoardState] = useState(initialState());
  function updateSquareValues(key: number) {
    const lastSquares = state.squares.slice();
    lastSquares[key] = (state.nextIsX)? '✕': '◯';
    
    // update "state"
    setBoardState({
      nextIsX: ! state.nextIsX,
      squares: lastSquares
    });
  
  }
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <div>next player: {(state.nextIsX)? '✕': '◯'}</div>
        {renderBoard({state, onValueChange: updateSquareValues })}
      </header>
    </div>
  );
}
function initialState(): BoardState {
  return {
      nextIsX: true,
      squares: Array(9).fill(null),
  };
}
export default App;
BoardHook.tsx
import React from 'react';
import './BoardHook.css';
import { renderSquare } from './SquareHook';
import { BoardState } from '../tic-tac-toe/Board';
export type BoardProps = {
    state: BoardState,
    onValueChange: (key: number) => void,
}
export function renderBoard(props: BoardProps): JSX.Element {
    return (
        <div className="board-row">
                {Array.from(Array(9).keys())
                    .map(i => (renderSquare({
                        key: i,
                        value: props.state.squares[i],
                        onClick: () => props.onValueChange(i)
                    })))}
            </div>
        );
}
SquareHook.tsx
import 'react-app-polyfill/ie11';
import 'react-app-polyfill/stable';
import React from "react";
import './SquareHook.css';
import { SquareValue } from '../tic-tac-toe/SquareValue';
export type SquareProps = {
    key: number,
    value: SquareValue,
    onClick: () => void,
};
export function renderSquare(props: SquareProps): JSX.Element {
    return (<button key={props.key} className="square"
            onClick={() => props.onClick()}>
            {props.value}
        </button>);
}
Side-effect
When the component was rendered or removed, or states were update, I could do something through "useEffect".
App.tsx
...
function App() {
  const [state, setBoardState] = useState(initialState());
  
  useEffect(() => {
    // when this component rendered, this function was called.
    console.log('load');
    document.title = (state.nextIsX)? '✕': '◯';
    // before remove this component, this function was called.
    return () => console.log('clean');
  },
  // only the "state" was updated, the functions inside "useEffect" were called.
  [state]);
...    
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <div>next player: {(state.nextIsX)? '✕': '◯'}</div>
        {renderBoard({state, onValueChange: updateSquareValues })}
      </header>
    </div>
  );
}
...
Limitation
"useState" and "useEffect" had to be called from top level component.
So I couldn't call them from BoardHook.tsx or SquareHook.tsx.
I had been afraid the top level component would had too much responsibilities.
I wanted to learn the architecture for React.
Get data from server(mock)
Next, I tried getting data from server.
In this time, I just used mock.
game.service.ts
import { SquareValue } from "../tic-tac-toe/SquareValue";
export async function getSquares(): Promise<Array<SquareValue>>{
    return await new Promise(resolve =>
        setTimeout(() => resolve(Array(9).fill(null)), 1000));
}
How to use async/await ?
First, I tried accessing the function from "useState".
App.tsx (failed)
...
import { getSquares } from './tic-tac-toe-hooks/game.service';
// in index.tsx, a compile error was occurred.
async function App() {
  const [state, setBoardState] = useState(async initialState());
...
}
async function initialState(): BoardState {
  return {
      nextIsX: true,
      squares: await getSquares(),
  };
}
And I got error.
Maybe because the component couldn't have async/await.
"useEffect" also couldn't use them directly.
App.tsx (failed)
...
function App() {
  const [state, setBoardState] = useState(initialState());
  // compile error
  useEffect(async () => {
    setBoardState({
        nextIsX: state.nextIsX,
        squares: await getSquares()
    });
  }, []);
...
I should do this.
App.tsx
...
function App() {
  const [state, setBoardState] = useState(initialState());
  useEffect(() => {
    const get = async () => {
      setBoardState({
        nextIsX: state.nextIsX,
        squares: await getSquares()
      });
    };
    get();
  }, []);
...
How to fetch data with React Hooks? - RWieruch
 
コメント
コメントを投稿