スキップしてメイン コンテンツに移動

[TypeScript] Try React + Hooks

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

コメント

このブログの人気の投稿

[Angular][ASP.NET Core] Upload chunked files

Intro I wanted to send files to Web application (made by ASP.NET Core). If the file size had been small, I didn't need do any special things. But when I tried to send a large file, the error was occurred by ASP.NET Core's limitation. Though I could change the settings, but I didn't want to do that, because I hadn't known the file sizes what would been actually using. So I splitted the data into chunks first, and sent them. After receiving all chunks, I merged them into one file. There might be some libraries or APIs (ex. Stream API) what did them automatically, but I couldn't find them. What I did [ASP.NET Core] Make CORS enabled [Angular] Split a large file into chunks [Angular][ASP.NET Core] Send and receive data as form data [ASP.NET Core] Merge chunks into one file [ASP.NET Core] Make CORS enabled Because the client side application(Angular) and the server side application(ASP.NET Core) had been separated, I had to make CORS(Cross-Origin Requests) ...

[Nest.js] Show static files

Intro I wanted to use Nest.js and WebRTC(node-webrtc). NestJS - A progressive Node.js framework Documentation | NestJS - A progressive Node.js framework And because I wanted to try with simple page(not use JavaScript frameworks), I added static HTML, CSS, JavaScript into a Nest.js project. Prepare Install First, I installed @nestjs/cli. First steps | NestJS - A progressive Node.js framework As same as last time , I couldn't do global install because I had used Volta. But I could installed by volta. volta install @nestjs/cli Create project nest new nest-web-rtc-sample volta pin node@12 Run npm start After doing "npm start", I could getting "Hello World!" from http://localhost:3000. Add static files I could add static files by two ways. @nestjs/serve-static First one of them was using "serve-static". Serve Static | NestJS - A progressive Node.js framework npm install --save @nestjs/serve-static And I needed adding a module into app.modu...

[Angular] Sending file with Observable and showing loading screen

Intro When I tried sending file data on last time, I had confused with "promise.then", "async/await" and "Observable". [Angular][ASP.NET Core] Upload chunked files So I wanted to distinct them, and this time, I tried to use "Observable" because HttpClient return Observable<any>. Call observables in order I sended file in these steps. Read file by FileReader Create directory for saving chunks send and saving chunks merge chunks to one file and delete chunks Each steps used the former steps result. So I could write by Promise.then like below. this.executeStep1() // return Promise<UploadResult> .then(result => this.executeStep2(result)) // return Promise<UploadResult> .then(result => this.executeStep3(result)) // return Promise<UploadResult> .catch(reason => console.log(reason)); Result I could write with pipe & flatMap. file-uploader.service.ts public upload(file: File): Observable<U...