はじめに
Realmの勉強のために、ReactNavigationの画面遷移を用いてじゃんけんアプリを作りました。その段階で結構なボリュームになってしまったので、じゃんけんアプリのみで記事を作ることにしました。
微力でも、じゃんけんアプリをReactNativeで作りたいという方の参考になればなと考えております。
なお、realmはまだインストールすらできていません……
簡単なアプリの解説
ごくありふれたじゃんけんアプリです。
初期画面から、自分の手の画像を押すと勝敗が判定されます。
プログラミングの本でチャプタ1や2で出てくるやつ感がすごいですね。
画面遷移に今回はReactNavigationを使用しました。インストール方法等は
公式ドキュメントを参考にしていただければと思います。
https://reactnavigation.org/docs/getting-started
画像はいらすとや様のサイトからもってきました。
https://www.irasutoya.com/
内容解説
初期画面
TopView.js
import React from 'react';
import {View, Text , SafeAreaView,StyleSheet ,Image, TouchableOpacity} from 'react-native';
const TopView = ({navigation}) => {
return (
<SafeAreaView style = {{flex :1}}>
<>
<View style = {styles.titleStyle}>
<Text style = {styles.titleText}>じゃんけん</Text>
</View>
<View style = {styles.cardView}>
<TouchableOpacity
onPress = {() =>navigation.navigate('Result',{ myCard :0})}>
<Image
style = {styles.cardStyle}
source = {require('../pic/janken_gu.png')}
/>
</TouchableOpacity>
<TouchableOpacity
onPress={() => navigation.navigate('Result',{ myCard :1})}>
<Image
style = {styles.cardStyle}
source = {require('../pic/janken_choki.png')}
/>
</TouchableOpacity>
<TouchableOpacity
onPress = {() => navigation.navigate('Result',{ myCard :2})}>
<Image
style = {styles.cardStyle}
source = {require('../pic/janken_pa.png')}
/>
</TouchableOpacity>
</View>
</>
</SafeAreaView>
)
};
export default TopView;
const styles = StyleSheet.create({
titleStyle: {
flex : 5,
alignItems : 'center',
margin: 20 ,
},
titleText: {
fontSize: 40 ,
},
cardView :{
flexDirection :'row',
flex : 4 ,
justifyContent: 'space-between',
margin : 15 ,
},
cardStyle :{
width : 100 ,
height :100 ,
},
});
TopViewでのわからんポイントとしては
1. 画面遷移の仕方がいまいちわからん
2. 画面遷移した際にpropsってどう渡すせばいいのかわからん
主にReactNavigationでつまりました。
1を解決したのは
https://reactnavigation.org/docs/navigating
公式ドキュメントでした。
正直まだ正しく仕様を理解したとは言い難いのですが……ひとまず、画面遷移する際にはnavigation props入れる必要があるという部分がわかり、解決しました。
2を解決したのは
https://reactnavigation.org/docs/params/
やはり公式ドキュメントでした。公式ドキュメントしか勝たん。
やり方として2つあるそうですが、navigation.navigate関数の第二引数に渡したい値を入力し、遷移後に渡す方法を採用しました。
2つとも主に
TopView.js
onPress = {() =>navigation.navigate('Result',{ myCard :0})}
の部分に当たります。
結果画面
ResultView.js
import React , { useEffect , useState } from 'react';
import {View,StyleSheet ,Image,Text } from 'react-native';
const TopView = ({route}) => {
const [card ,setCard] = useState();
const {myCard} = route.params;
const resultMyCard = JSON.stringify(myCard)
//CPUの手を最初に決定
useEffect(() => {
const cardNumber = Math.floor(Math.random()*3)
setCard(cardNumber)
},[])
const cpuCard = card == 0 ? "gu"
:card == 1 ? "choki"
:"pa"
//自分の手を決定
const myCardPic = resultMyCard == 0 ? require('../pic/janken_gu.png')
: resultMyCard == 1 ? require('../pic/janken_choki.png')
: require('../pic/janken_pa.png')
//勝敗の決定
const point = resultMyCard - card
const judge = point == 0 ? "あいこです"
:point == -1 ? "あなたの勝ちです"
:point == 2 ? "あなたの勝ちです"
:"あなたの負けです"
return(
<>
{
(() => {
if (cpuCard == "gu") {
return(
<>
<View style = {styles.cpuView}>
<Image
style = {styles.cardStyle}
source = {require('../pic/janken_gu.png')}
/>
</View>
<View style = {styles.titleStyle}>
<Text style = {styles.titleText}>{judge}</Text>
</View>
<View style = {styles.cpuView}>
<Image
style = {styles.cardStyle}
source = {myCardPic}
/>
</View>
</>
);
} else if(cpuCard == "choki") {
return (
<>
<View style = {styles.cpuView}>
<Image
style = {styles.cardStyle}
source = {require('../pic/janken_choki.png')}
/>
</View>
<View style = {styles.titleStyle}>
<Text style = {styles.titleText}>{judge}</Text>
</View>
<View style = {styles.cpuView}>
<Image
style = {styles.cardStyle}
source = {myCardPic}
/>
</View>
</>
);
}
else if(cpuCard == "pa"){
return(
<>
<View style = {styles.cpuView}>
<Image
style = {styles.cardStyle}
source = {require('../pic/janken_pa.png')}
/>
</View>
<View style = {styles.titleStyle}>
<Text style = {styles.titleText}>{judge}</Text>
</View>
<View style = {styles.cpuView}>
<Image
style = {styles.cardStyle}
source = {myCardPic}
/>
</View>
</>
)
}
})()
}
</>
)
}
export default TopView;
const styles = StyleSheet.create({
titleStyle: {
flex : 5,
alignItems : 'center',
justifyContent: 'center',
margin: 20 ,
},
titleText: {
fontSize: 40 ,
},
cpuView :{
flexDirection :'row',
flex : 3 ,
justifyContent:'center',
margin : 15 ,
paddingTop: 30,
},
cardStyle :{
width : 100 ,
height :100 ,
},
});
変数の名前とかはかなり適当ではありますが、結果の画面です。
この部分でのわからんポイントとしては
1. return内でifってどうやるんだ
2. 画像のrequire内で文字列の結合ができない
3. 三項演算子で文字列判定がうまくできない
4. Navigationで渡したpropsどう受け取る
1を解決したのは
React JSX の中で if で分岐させたい
この記事を参照にallow関数でif文を呼び出し、分岐させました。記事を書いている今は三項演算子で分岐させたほうがよかったかもと思いました。すごいうっすらとした理解ですがなんとか動きました。
2を解決したのは
【React Native】require()を使ってローカルにある画像を表示する
require内では仕様として文字列の結合ができないそうなので、三項演算子を用いて場合分けを行い自分の手の反映を行いました。
ResultView.js
const myCardPic = resultMyCard == 0 ? require('../pic/janken_gu.png')
: resultMyCard == 1 ? require('../pic/janken_choki.png')
: require('../pic/janken_pa.png')
の部分です。
3は解決できませんでした。
なので、文字列ではなく数字に置き換えて場合分けを行いました。また、三項演算子の複数条件については、正しく分岐されなかったため非常に乱暴な方法ではありますが、2度に分けて反映させました。いまいち三項演算子のうまい活用の方法がわからない……
4は再びReactNavigation公式ドキュメント
https://reactnavigation.org/docs/params/
を参考に
ResultView.js
const {myCard} = route.params;
const resultMyCard = JSON.stringify(myCard)
paramsをResultViewで受け取るように設定しました。
最後に
記事にもコードの内容にもガバが多すぎる……となっているため、半年とか1年後にもう少し知識をつけたら書き直します。