この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
現在開催中の WWDC20 の「SwiftUI」に関するセッションを見ていて「これってReactに近いのでは...」と感じました。そこで実際に SwiftUI と 私が現在業務で使用している React Native for Web + TypeScript を使って簡単な画面を作って比べてみたのでご紹介します。
実装したもの
- 「+」「ー」ボタンで数値を操作する簡単なカウンター画面
- SwiftUI版は「Xcode Version 11.4.1」を、React Native for Web版は「Version 0.12.2」を使用しました。
- パターン1(1つの画面で実装したもの)とパターン2(カウンターを部品化し、2画面に分けたもの)の2つのパターンで実装しました。
パターン1(1つの画面で実装したもの)
SwiftUI版
SwiftUIで使用できるUIコンポーネント(Button、Textなど)を使って「+」「ー」ボタンと、カウンター値を表示するTextを定義しました。また、View内の一時的な状態を管理するための変数として@Stateをつけたプロパティcount
を定義し、「+」「ー」ボタンのタップ時に値を更新しています。
SimpleCounter.swift
import SwiftUI
struct SimpleCounter: View {
@State private var count = 0
var body: some View {
HStack(spacing: 25) {
Button("ー") {
if (self.count > 0) {
self.count -= 1
}
}
.font(.largeTitle)
.foregroundColor(.black)
Text("カウント: \(self.count)")
.font(.title)
Button("+") {
self.count += 1
}
.font(.largeTitle)
.foregroundColor(.black)
}
}
}
実行結果は以下のようになりました。
React Native for Web + TypeScript版
こちらはReact Native for Webで用意されているコンポーネント(View、Text、TouchableOpacityなど)を使って画面を定義しました。先ほどのSwiftUI版で@State
をつけて宣言していた変数と同じ役割をするものが、React.useState()を使って宣言した変数になります。これは[count, setCount]
とセットで定義する変数で、count
が現在の値を保持し、setCount
を使って値の更新を行います。また、useState(0)の0は初期値
です。
SimpleCounter.tsx
import React from 'react'
import { Text, TouchableOpacity, StyleSheet, View } from 'react-native'
const SimpleCounter = () => {
const [count, setCount] = React.useState(0)
return (
<View style={styles.counter}>
<TouchableOpacity
onPress={() => { if (count > 0) { setCount(count - 1) } }}
>
<Text style={styles.text}>ー</Text>
</TouchableOpacity>
<Text style={styles.text}>{`カウント: ${count}`}</Text>
<TouchableOpacity onPress={() => setCount(count + 1)}>
<Text style={styles.text}>+</Text>
</TouchableOpacity>
</View>
)
}
const styles = StyleSheet.create({
counter: {
marginTop: 300,
flexDirection: 'row',
alignItems: 'stretch',
justifyContent: 'center',
},
text: {
fontSize: 24,
marginHorizontal: 8
}
})
export default SimpleCounter
こちらの実行結果は以下の通りとなり、SwiftUI版とほぼ同じUIとなっています。
パターン2(カウンターを部品化し、2画面に分けたもの)
SwiftUI版
パターン1では@State
で定義したプロパティcount
を「+」「ー」ボタンタップ時に更新していましたが、パターン2では、カウンターの箇所を部品化し、親の画面から値を受け取って表示・更新するようにしています。親の画面から値を受け取るための変数が@Bindingをつけたカウンター側にあるプロパティcount
となります。@Bindingをつけることで、親の画面から受け取った値を読み書きできるようになります。
CounterScreen.swift
import SwiftUI
struct CounterScreen: View {
@State private var count = 0
var body: some View {
Counter(count: $count)
}
}
struct Counter: View {
@Binding var count: Int
var body: some View {
HStack(spacing: 25) {
Button("ー") {
if (self.count > 0) {
self.count -= 1
}
}
.font(.largeTitle)
.foregroundColor(.black)
Text("カウント: \(self.count)")
.font(.title)
Button("+") {
self.count += 1
}
.font(.largeTitle)
.foregroundColor(.black)
}
}
}
実行結果は、パターン1と同じになります。
React Native for Web + TypeScript版
こちらのReact Native for Web版ではSwiftUI版で@Binding
を使っていた箇所をpropsという仕組みを使って実現します。カウンター側は、親画面で定義したcount(カウンター値の読み取り用変数)
, setCount(カウンター値を更新するための関数)
を、propsを使って受け取ります。そして、props.count
を使ってカウンター値の表示をし、props.setCount()
を使って値の更新を行ないます。
CounterScreen.tsx
import React from 'react'
import { Text, TouchableOpacity, StyleSheet, View } from 'react-native'
const CounterScreen = () => {
const [count, setCount] = React.useState(0)
return (
<Counter count={count} setCount={setCount} />
)
}
type CounterProps = {
count: number
setCount: (count: number) => void
}
const Counter = (props: CounterProps) => {
return (
<View style={styles.counter}>
<TouchableOpacity
onPress={() => { if (count > 0) { props.setCount(count - 1) } }}
>
<Text style={styles.text}>ー</Text>
</TouchableOpacity>
<Text style={styles.text}>{`カウント: ${props.count}`}</Text>
<TouchableOpacity onPress={() => props.setCount(count + 1)}>
<Text style={styles.text}>+</Text>
</TouchableOpacity>
</View>
)
}
const styles = StyleSheet.create({
counter: {
marginTop: 300,
flexDirection: 'row',
alignItems: 'stretch',
justifyContent: 'center',
},
text: {
fontSize: 24,
marginHorizontal: 8
}
})
export default CounterScreen
こちらの実行結果も、パターン1と同じです。
まとめ
SwiftUIとReact Native for Webを使って簡単な画面を実装した内容についてご紹介しました。それぞれ用意されたコンポーネントを使って画面を組み立てていく点や、画面内の状態を保持する変数の役割・使い方などについて、類似していると感じました。
この記事がどなたかのお役に立てば幸いです。