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

[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] Use WebSocket with ws

Intro Until last time , I had used node-web-rtc to try WebRTC. But because the example was a little complicated for I understood the core functions of using WebRTC. So I look for other frameworks or libraries. PeerJS is a famous library for WebRTC. peers/peerjs: Peer-to-peer data in the browser. - GitHub peers/peerjs-server: Server for PeerJS - GitHub PeerJS - Simple peer-to-peer with WebRTC A problem is I don't know how to integrate to the Nest.js project. I couldn't find examples. So I don't choose at least this time. What shall I choose? According MDN, WebRTC doesn't specify strictly what technology is used on server application for connecting two devices. Signaling and video calling - Web APIs | MDN But in many examples include MDN's one use WebSocket. samples-server/s/webrtc-from-chat at master · mdn/samples-server · GitHub So I try WebSocket in the Nest.js project. Use WebSocket in a Nest.js project Nest.js has a function for using We

[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