1923

都内と業界の隅っこで生活しているエンジニアのノート

React Native (Expo) から Azure Functions と SignalR Service のクイック スタートのサンプルアプリを試してみた

クイック スタート:C# を使用した Azure Functions と SignalR Service によるチャット ルームの作成のクライアントテストアプリケーションを Expoで作成して試してみます。

docs.microsoft.com

記事の中で紹介されている サンプルのシングル ページ Web アプリケーション を React Native + Expo 用にちょっと書き直しです。

クリックスタートに従って、ブラウザで動くところまで準備して

  1. Azure SignalR Service作成
  2. サンプル アプリケーションの準備と実行
  3. Web アプリケーションで動作確認

クライアントを作成です。

Expo のプロジェクト作成 (TypeScript)とパッケージを必要なパッケージの追加

expo init chat
cd chat
npm i @microsoft/signalr
npm i axios

あとは出来たプロジェクトのApp.tsxをちょい修正してサンプルアプリは完了。

import React from 'react'
import { StyleSheet, Text, TextInput, Button, SafeAreaView, FlatList } from 'react-native'
import { HubConnectionBuilder } from '@microsoft/signalr'
import axios from 'axios'

const name = 'ヤス'
const apiBaseUrl = 'http://192.168.XXX.XXX:7071'

export default class App extends React.Component {
  state:IState = {
    isReady: false,
    count: 1,
    text: '',
    messages: []
  };

  increment (): number {
    const count = this.state.count + 1
    this.setState({ count: count })
    return count
  }

  componentDidMount = () => {
    const hubConnection = new HubConnectionBuilder()
      .withUrl(`${apiBaseUrl}/api`)
      .build()

    hubConnection.onclose(() => console.log('disconnected'))
    hubConnection
      .start()
      .then(() => this.setState({ isReady: true }))

    hubConnection.on('newMessage', (m:IMessage) => {
      const key = this.increment()
      const messages = this.state.messages.slice() as Message[]
      const message = new Message(key.toString(), m.sender, m.text)
      messages.unshift(message)
      this.setState({ messages: messages })
    })
  }

  sendMessage = (text:string) => {
    axios.post(`${apiBaseUrl}/api/messages`, {
      sender: name,
      text: text
    }).then(_ => {})
    this.setState({ text: '' })
  }

  render () {
    if (!this.state.isReady) {
      return <SafeAreaView style={styles.container}>
        <Text>Connecting</Text>
      </SafeAreaView>
    } else {
      return <SafeAreaView style={styles.container}>
        <Text>Serverless Chat</Text>
        <TextInput
          style={styles.textBox}
          onChangeText={value => this.setState({ text: value })}
          value={this.state.text}
        />
        <Button
          title="送信"
          onPress={() => {
            this.sendMessage(this.state.text)
          }}
        />
        <FlatList
          data={this.state.messages}
          renderItem={({ item }) =>
            <Text style={styles.item}>
              {item.sender} : {item.text}
            </Text>}
        />
      </SafeAreaView >
    }
  }
}

class Message implements IMessage {
  key: string
  sender: string
  text: string

  constructor (key: string, sender: string, text: string) {
    this.key = key
    this.sender = sender
    this.text = text
  }
}

interface IMessage {
  sender: string
  text: string
}

interface IState {
  isReady: boolean
  count: number
  text: string
  messages: Message[]
}

const styles = StyleSheet.create({
  container: {
    flex: 1
  },
  item: {
    padding: 10,
    height: 44
  },
  textBox: {
    height: 50,
    margin: 10,
    padding: 10,
    borderColor: 'gray',
    borderWidth: 1
  }
})

サンプルのシングル ページ Web アプリケーションと同じように動きました。

f:id:taka1923:20200813055457p:plainf:id:taka1923:20200813055014p:plain