React NativeクライアントからWebSocketでnodejsのサーバと通信する

# React NativeでWebSocket通信する
　
App Engine フレキシブル環境にnodejsでWebSocketサーバーを立てて、そこに対してReact Nativeクライアントから接続します。ほぼサンプルコードをもとに作成できたのですが、ちょっとだけ詰まりました。ここではローカルでnodejsのサーバと通信するところまで書きます。

## サーバー

こちらを参考に、
https://github.com/GoogleCloudPlatform/nodejs-docs-samples/tree/master/appengine/websockets

とりあえずローカルでそのまま実行してみます。
クローンして、移動して、実行

    git clone https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git
    cd nodejs-docs-samples/appengine/websockets/
    yarn install && yarn start

これで、ブラウザからlocalhost:8080にアクセスすると、WebSocketのメッセージクライアントの画面が表示できるので、二つのブラウザで開いて実際にメッセージのやりとりを試すことができます。このサーバーにReactNativeのクライアントからつなぎます。

![スクリーンショット 2020-08-09 15.54.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64097/13fd620b-1f60-d092-1bbd-117a25696a70.png)


## クライアント

クライアントを簡単に作成してサーバーに繋いでみます。expo-cliを[インストール](https://docs.expo.io/workflow/expo-cli/)し、

    expo init

から、 `blank`プロジェクトを作成し、`プロジェクトフォルダ`に移動して、`App.tsx`を次のように変更します。（WebSocket試すための最低限のサンプルです）。スマホ実機からQRコードを読み込んでテストする場合のURLはローカルマシンのIPを指定します。（サーバー側をローカルで実行する場合）。
以下では、socket.io-clientを利用しています。標準ライブラリの[WebSocket](https://developer.mozilla.org/ja/docs/Web/API/WebSockets_API)を使っても動くんじゃないかと思うんですが、なんでか上手くいかなかったです。

    import { StatusBar } from 'expo-status-bar';
    import React from 'react';
    import { StyleSheet, Text, View, TextInput, TouchableOpacity, Alert } from 'react-native';
    import socketIOClient from "socket.io-client";

    export default class App extends React.Component {

    socket

    constructor(props) {
        super(props);
        this.state = {text: ""}
    }

    componentDidMount(){
    this.socket = socketIOClient("ws://192.xxx.xxx.xxx:8080");
    //  this.socket = socketIOClient("https://'app engine project id'.appspot.com");

    const _that = this;

    this.socket.on('message', function(data){ 
        console.log('recieve', data)
        Alert.alert("Message recieved. " + JSON.stringify(data))
    });
    }
    
    sendMessage(){
        if(!this.state.text){
        Alert.alert('Please input message')
        return;
        }
        this.socket.emit('message', this.state.text);
    }

    render() {
        return (<View style={styles.container}>
        <TextInput onChangeText={(text) => this.setState({text: text}) } 
        style={styles.input}></TextInput>
        <TouchableOpacity style={styles.button}
        onPress={() => this.sendMessage()}><Text>Click to send Message</Text></TouchableOpacity>
        </View>)
    }
    }

    const styles = StyleSheet.create({
    container: {
        flex: 1,
        backgroundColor: '#fff',
        alignItems: 'center',
        justifyContent: 'center',
    },
    input: {
        height: 40, borderColor: 'gray', borderWidth: 1 , width: '100%'
    },
    button: {
        backgroundColor: "#888",  height: 40, width: "100%", marginTop: 10, alignItems: 'center',padding: 'auto'
    }
    });

その後、`yarn add socket.io-client`でライブラリ追加して、 `yarn start`でクライアントを起動する。

## AppEngine

このサンプルサーバーをAppEngineにデプロイのは、すでにサーバーのソースにapp.ymlが含まれているので、フォルダ、`nodejs-docs-samples/appengine/websockets/`から、`gcloud app deploy`叩くだけです。ReactNativeクライアントの方は、URLを実際のURLに変更します。
**使わなくなったサーバーは直ぐに削除した方が良いです。**私はしばらく上げっぱなしにしてしまったら、数千円利用料かかりました。AppEngineのflux環境はまあまあコストかかるんですかね？WebSocketが関係している？のでしょうか