Reactを使用したオンラインゲームの接続


この文章は「[オンラインゲームの作成と学習のインタラクション]」課のまとめです.

1.React Hooksが登場したきっかけ


既存のReactでは、以下のように関数型を使用する場合、クラスのsetStateまたはstateは使用できないため、setStateまたはstateを使用しない構文にのみ使用されます.
const GuGuDan = () => {
            return <div>Hello, Hooks</div>;
}
しかし、関数型内でstateを使用することがますます要求されるにつれて、Reactは関数型素子内でstateを使用することを改善した.それがいわゆる「React Hooks」です.クラスコンポーネントベースのレスポンスコードを中心としていたが、最近ではレスポンス式ドキュメントでクラスコンポーネントを使用するのではなく、Hooksを使用することを推奨している.Hooksの基本フォーマットは次のとおりです.
// 아래 코드는 웹팩을 사용했다고 가정한 코드이다.

const GuGuDan = () => {
	const [first, setFirst] = useState(Math.ceil(Math.random()*9));
	const [second, setSecond] = useState(Math.ceil(Math.random()*9));
	// 필요한 추가적인 state들 작성	

	return (
		<>
			<div>{first}곱하기{second}?</div>
		</>
	);
}

2.Refの使い方


Refの場合、クラスとHooksの使い方が異なることに注意してください.

2.1.クラス構成部品のRef


(1)
const onRefInput = (c) => { this.taewoong = c }
this.taewoong.focus();
<input ref={ this.onRefInput } ... />
(2)
import { createRef } from 'react';
inputRef = createRef();
this.inputRef.current.focus();
<input ref={this.inputRef} ... />
第2の方法と同様に、クラスのrefはHooksのrefと同様に使用することができる.ただし、2つ目の方法と同じ方法ではrefに他のアクションを追加することはできません.そのため、refをより詳細に利用したい場合は、(1)の方法でrefを作成できます.

2.2.HooksのRef

const React = require('react');
const { useRef } = React;
const inputRef = useRef(null);
inputRef.current.focus();
<input ref={ inputRef } ... />
HooksのRefについては、一般値を記憶する方法もあるが、後述する.

3.Hooksの再読み込み


以前に符号化されたclass gudan素子をhooksに変換し、以下に示す.
const React = require('react');
const { useState, useRef } = React;

const GuGuDan = () => {
    const [first, setFirst] = useState(Math.ceil(Math.random() * 9));
    const [second, setSecond] = useState(Math.ceil(Math.random() * 9));
    const [value, setValue] = useState('');
    const [result, setResult] = useState('');
    const inputRef  = useRef();

    const onChangeInput = (e) => {
        setValue(e.target.value);
    };

    const onSubmitForm = (e) => {
        e.preventDefault();
        if(parseInt(value) === first * second){
            setResult('정답: '  + value);
            setFirst(Math.ceil(Math.random() * 9));
            setSecond(Math.ceil(Math.random() * 9));
            setValue('');
            inputRef.current.focus();
        } else {
            setResult('떙');
            setValue('');
            inputRef.current.focus();
        }
    }

    return  <>
        <div>{first}곱하기{second}?</div>
        <form onSubmit={onSubmitForm}>
            <input ref={inputRef} type="number" onChange={onChangeInput} value={value} />
            <button>입력!</button>
        </form>
        <div id="result">{result}</div>
    </>
}

module.exports = GuGuDan;
デフォルトでは、Hooksはステータスの変更に伴って再ロードされ、矢印関数のすべての部分が再ロードされます.すなわち、class素子に比べて、再ロード時に関数全体が再生成されるため、速度が遅くなる可能性があります.stateが変更されるたびに再ロードされる場合は、次のような状況ですか?下記のstateは4回交換していますので、4回再ロードできますか?
const onSubmitForm = (e) => {
      e.preventDefault();
      if(parseInt(value) === first * second){
          **setResult('정답: '  + value);
          setFirst(Math.ceil(Math.random() * 9));
          setSecond(Math.ceil(Math.random() * 9));
          setValue('');**
          inputRef.current.focus();
      } else {
          setResult('떙');
          setValue('');
          inputRef.current.focus();
      }
  }
答えは「いいえ」です.classコンポーネントでもそうですが、userStateクラス(上のsetResult、setFirstなど)は非同期関数です.したがって、パラレル実行なので、4回stateを変更しても1回しかレンダリングできません.
...

<body>
    <div id="root"></div>
    <script type="text/babel">
        const e = React.createElement;
        class Taewoong extends React.Component {
            state = {
                c: 0,
            };
            
            onClick = () => {
                this.setState({
                    c: this.state.c + 1,
                });
                this.setState({
                    c: this.state.c + 1,
                });
                this.setState({
                    c: this.state.c + 1,
                });
            };
            render() {
                return (
                    <React.Fragment>
                        <button onClick={this.onClick}>{this.state.c}</button>
                    </React.Fragment>
                );
            }
        }
    </script>
    <script type="text/babel">
        ReactDOM.render(<Taewoong />, document.querySelector('#root'));
    </script>
</body>
たとえば、上のコードのようにsetStateと書くと、cは3になる可能性がありますが、setStateは並列に実行されるため、3にはならず、1になる可能性もあります.

4.Webパッケージとは何か&Webパッケージとホットロードの使い方


Webパッケージは、最新のフロントエンドフレームワークで最も一般的なモジュールバンドルパッケージです.モジュールバンドルパッケージとは、Webアプリケーションを構成するすべてのリソース(HTML、CSS、JavaScript、Imagesなど)を各モジュールと見なし、それらを組み合わせて結果を作成するツールです.
簡単に言えば、Webパッケージを使う理由は以下の通りです!
簡単にするために、フェイスブックを例に挙げましょう.Facebookには2万個を超えるコンポーネント(JavaScript)があるそうです.では、この2万個の素子を1ページにまとめるにはどうすればいいのでしょうか.この機能を実現するのがWebパッケージです.構成部品を結合し、重複を除去し、babelを適用します.
Webパッケージとホットロードの使用
(1)ノードを敷く.
(2)npm initでパッケージ化する.jsonの生成
(3) npm i react react-dom
(4) npm i -D webpack webpack-cli
(5)npx webpackでwebpackを実行できる.
(6)ただし,webpackでJSXを使用するには,以下のbabelに関するモジュールをインストールする必要がある.
(7) npm i -D @babel/core @babel/preset-env @babel/preset-react babel-loader
(8)以降のwebpack.config.js設定(以下で整理)
(9) package.jsonの「scripts」は以下のように設定され、num run devホットロードを使用します.
// package.json

{
  "name": "webpackgo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  ***"scripts": {
    "dev": "webpack"
  },***
  "author": "taewoong",
  "license": "MIT",
  "dependencies": {
    "react": "^17.0.2",
    "react-dom": "^17.0.2"
  },
  "devDependencies": {
    "@babel/core": "^7.14.6",
    "@babel/preset-env": "^7.14.7",
    "@babel/preset-react": "^7.14.5",
    "babel-loader": "^8.2.2",
    "webpack": "^5.44.0",
    "webpack-cli": "^4.7.2"
  }
}

webpack.config.jsの基本設定は以下の通りです.Webpack 5バージョンに変更すると、いくつかの設定が変わりますのでご注意ください!
const path = require('path');
const webpack = require('webpack');

module.exports = {
  mode: 'development',
  devtool: 'inline-source-map', // hidden-source-map
  resolve: {
    extensions: ['.jsx', '.js'],
  },

  entry: {
    app: './client',
    // 나중에 app: ['./client.jsx', './csas.css', './asdadaf.json'], 
		// 와 같이 여러 형식의 파일들을 app.js에 하나로 합쳐줄 수 있는데, 
		// 파일명을 일일이 쓰기 귀찮다면 위에 resolve: {}에 확장자명을 넣어주면 된다. 
		// 그럼 알아서 웹팩에서 각각의 확장자명의 파일들을 찾아준다.
  },
  module: {
    rules: [{
      test: /\.jsx?$/,
      loader: 'babel-loader',
      options: {
        presets: [
          ['@babel/preset-env', {
            targets: {
              browsers: ['> 1% in KR'], // browserslist
							// 원하는 브라우저 버전에만 바벨을 적용할 수도 있다.
              // 많은 브라우저를 지원할수록 속도가 그만큼 느려지기 때문
	            // 위 코드는 한국에서 5% 이상 지원하는 브라우저를 타겟으로 하는 것이다.
              // https://github.com/browserslist/browserslist
							// 위 주소에서 더 자세한 정보를 확인할 수 있다.
            },
            debug: true,
          }],
          '@babel/preset-react',
        ],
        plugins: [],
      },
    }],
  },
  output: {
    filename: 'app.js',
    path: path.join(__dirname, 'dist'),
  },
};
すべて設定が完了すると、以下のようにreactとreact-domが読み込まれます.
(1)
import React from 'react';
import ReactDOM from 'react-dom';

(2)
const React = require('react');
const ReactDOM = require('react-dom');

두 방법 모두 가능하다. (1)은 Next.js 쓸 때 자주 쓰고, (2)는 Node.js 쓸 때 자주 쓰는 것 같다.
create-react-appを使用すると、上記の設定は自動的に完了しますが、create-react-appを最初から使用すると、プロセスが正確に理解できません.このように自分で勉強してからCRAを書いたほうがいいです.

5.復習問題(答えは下)


次のgugugudanコンポーネントを作成するコードを作成してください.(Hooksを使用)

  • Webパッケージを使用して1番コードを実行するコードを教えてください.

  • Webパッケージを使用して、次の文字ジャッキーコードを作成します(Hooksを使用します).


  • Webパッケージを使用して、次の文字ジャッキーコードを作成します(Classを使用してください)

  • 6.さらに考える



    後の概念をもう一度見てから考えます.上図では、ハイライトはレンダリングを意味します.現在のinputウィンドウに数値を入力した場合、inputウィンドウの「2に2を乗じた」または次の「正解」セクションはレンダリングされる必要はありませんが、レンダリングされます.このような状況は、今後のパフォーマンスの問題を引き起こす可能性があります.そこで、これは解決しなければならない問題と考えて、解決策を考えてみましょう.

    7.復習問題が正しい

  • 1番正解
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
        <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
        <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
        <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
    </head>
    <body>
        <div id="root"></div>
        <script type="text/babel">
    
        const GuGuDan = () => {
    
            const [first, setFirst] = React.useState(Math.ceil(Math.random()*9));
            const [second, setSecond] = React.useState(Math.ceil(Math.random()*9));
            const [value, setValue] = React.useState('');
            const [result, setResult] = React.useState('');
            const inputRef = React.useRef();
    
            const onChangeInput = (e) => {
                setValue(e.target.value);
            };
    
            const onSubmitForm = (e) => {
                e.preventDefault();
                if(parseInt(value) === first * second){
                    setResult('정답: '  + `${first} 곱하기 ${second}${first * second}!`);
                    setFirst(Math.ceil(Math.random()*9));
                    setSecond(Math.ceil(Math.random()*9));
                    setValue('');
                    inputRef.current.focus();
                } else {
                    setResult('땡');
                    setValue('');
                    inputRef.current.focus();
                }
            }
    
            return <React.Fragment>
                <div>{first}곱하기{second}?</div>
                <form onSubmit={onSubmitForm}>
                    <input ref={inputRef} type="number" onChange={onChangeInput} value={value} />
                    <button>입력!</button>
                </form>
                <div>{result}</div>
                </React.Fragment>
        }
        </script>
        <script type="text/babel">
            ReactDOM.render(<GuGuDan/>, document.querySelector('#root'));
        </script>
    </body>
    </html>
  • 2番正しいお客様に回答します.jsx
    const React = require('react');
    const ReactDOM = require('react-dom');
    
    const GuGuDan = require('./GuGuDan');
    
    ReactDOM.render(<GuGuDan/>, document.querySelector('#root'));
    GuGuDan.jsx
    const React = require('react');
    const { useState, useRef } = React;
    
    const GuGuDan = () => {
    
        const [first, setFirst] = useState(Math.ceil(Math.random()*9));
        const [second, setSecond] = useState(Math.ceil(Math.random()*9));
        const [value, setValue] = useState('');
        const [result, setResult] = useState('');
        const inputRef = useRef();
        
        const onSubmitForm = (e) => {
            e.preventDefault();
            if(parseInt(value) === first * second){
                setResult('정답: '  + `${first} 곱하기 ${second}${first * second}!`);
                setFirst(Math.ceil(Math.random()*9));
                setSecond(Math.ceil(Math.random()*9));
                setValue('');
                inputRef.current.focus();
            } else {
                setResult('땡');
                setValue('');
                inputRef.current.focus();
            }
        }
    
        const onChangeInput = (e) => {
            setValue(e.target.value);
        }
    
        return (
            <>
            <div>{first}곱하기{second}?</div>
            <form onSubmit={onSubmitForm}>
                <input ref={inputRef} type="number" onChange={onChangeInput} value={value} />
                <button>입력!</button>
            </form>
            <div>{result}</div>
            </>
        )
    }
    
    module.exports = GuGuDan;
    index.html
    <!DOCTYPE html>
    <html lang="ko">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>GuGuDan</title>
    </head>
    <body>
        <div id="root"></div>
        <script src="./dist/app.js"></script>
    </body>
    </html>
  • 3番正解クライアント.jsx
    const React = require('react');
    const ReactDOM = require('react-dom');
    
    const WordRelay = require('./WordRelay');
    
    ReactDOM.render(<WordRelay/>, document.querySelector('#root'));
    WordRelay.jsx
    const React = require('react');
    const { useState, useRef } = React;
    
    const WordRelay = () => {
    
        const [word, setWord] = useState('제로초');
        const [value, setValue] = useState('');
        const [result, setResult] = useState('');
        const inputRef = useRef();
    
        const onSubmitForm = (e) => {
            e.preventDefault();
            if(word.substr(-1) === value.substr(0,1)){
                setWord(value);
                setResult('딩동댕');
                setValue('');
                inputRef.current.focus();
            } else {
                setResult('땡');
                setValue('');
                inputRef.current.focus();
            }
        }
    
        const onChangeInput = (e) => {
            setValue(e.target.value);
        }
    
        return (
            <>
            <div>{word}</div>
            <form onSubmit={onSubmitForm}>
                <label htmlFor="taewoong">글자를 입력하세요 : </label>
                <input ref={inputRef} type="text" onChange={onChangeInput} value={value} />
                <button>입력!</button>
            </form>
            <div>{result}</div>
            </>
        )
    }
    
    module.exports = WordRelay;
    index.html
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
    </head>
    <body>
        <div id="root"></div>
        <script src="./dist/app.js"></script>
    </body>
    </html>
  • 4番正しいお客様に回答します.jsx
    const React = require('react');
    const ReactDOM = require('react-dom');
    
    const WordRelay = require('./WordRelayClass');
    
    ReactDOM.render(<WordRelayClass/>, document.querySelector('#root'));
    WordRelayClass.jsx
    import React, { Component } from 'react';
    
    class WordRelay extends Component {
    
        state = {
            word: '강태웅',
            value: '',
            result: '',
        }
    
        onSubmitForm = (e) => {
            e.preventDefault();
            if (this.state.word[this.state.word.length-1] === this.state.value[0]) {
                this.setState({
                    word: this.state.value,
                    value: '',
                    result: '딩동댕',
                });
                this.taewoong.focus();
            } else {
                this.setState({
                    value: '',
                    result: '땡',
                });
                this.taewoong.focus();
            }
        }
    
        onChangeInput = (e) => {
            this.setState({
                value: e.target.value,
            });
        }
    
        onRefInput = (c) => {this.taewoong = c};
    
        render() {
            return (
                <>
                    <div>{this.state.word}</div>
                    <form onSubmit={this.onSubmitForm}>
                        <input ref={this.onRefInput} type="text" value={this.state.value} onChange={this.onChangeInput} />
                        <button>입력</button>
                    </form>
                    <div>{this.state.result}</div>
                </>
            );
        }
    }
    
    export default WordRelay;
    index.html
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
    </head>
    <body>
        <div id="root"></div>
        <script src="./dist/app.js"></script>
    </body>
    </html>