js初心者がReactNativeを使ってみた

js初心者がReactNativeを使ってみた:


はじめに

インターンの事前課題で React Native を使ってネイティブアプリを作る機会に恵まれました。うろ覚えではこの事前課題を突破すると面接に呼ばれて、その面接を突破するとインターンでコードレビューとかしてくれる感じだったと思います(わかる人にはどの企業かわかるかもね)。今はまだ事前課題提出した段階です。

企業側としては React Native 経験がない学生を想定してくれているらしく、参考になるサイトを教えてくれたり、LINEでガンガン質問に答えたりしてくれます。JavaScriptそこそこ使ってるつもりだったけど何もわかってないんだなあってよくわかったし、デバッグのときの考え方、注目する部分などなど勉強になることがとても多かったです。

インターン課題自体のソースコードは公開できないと思いますが、ここで得た学び・ハマった点をできるだけ抽象化して記録しておこうと思います。(もし成果物を公開していいようだったら追記します!)


React Native とは

簡単に言うと、「JavaScriptだけでネイティブアプリを作れるフレームワーク」ってことになるんでしょうか。本来ネイティブアプリを作る際には、iOSアプリだとSwiftやObjective-C、AndroidだとJavaやKotlinという感じでOSによって使う言語が違うわけですが、React Native のすごいところは「同じソースコードで両方のOSに対応できる」点だと思います。処理はJavaScript、配置はXMLのように書けるので割と直感的ですし、App.jsを編集してシミュレーターを更新するだけで更新内容が確認できます。console.logをChromeのコンソールに表示させることもできますし、かなり作りやすい印象を受けました。


学び / ハマった点


チュートリアルは偉大

今更かって言われると思いますが、ステップアップしながら教えてくれるチュートリアルって本当にありがたいですね。React Nativeのチュートリアルは英語ですが、単語調べながらゆっくり読んでいけば十分理解できると思います(僕ができたので)。React Native のチュートリアルはこちら: https://facebook.github.io/react-native/docs/getting-started


トップレベルの要素はひとつ

https://utano.jp/entry/2016/07/react-adjacent-jsx-elements-must-be-wrapped-in-an-enclosing-tag/ に詳しい説明がありますが、Reactの仕様上このようなコードを書くと怒られます。

import React, { Component } from 'react'; 
import { View, Text } from 'react-native'; 
 
export default class App extends Component { 
    render() { 
        return ( 
            <View> 
                <Text>1つめのView</Text> 
            </View> 
            <View> 
                <Text>2つめのView</Text> 
            </View> 
        ); 
    } 
} 
エラーは Adjacent JSX elements must be wrapped in an enclosing tag です。この場合、トップレベルにViewコンポーネントが2つ並んでいるので、この2つをまとめるコンポーネントが必要になります。

import React, { Component } from 'react'; 
import { View, Text } from 'react-native'; 
 
export default class App extends Component { 
    render() { 
        return ( 
            <View> 
                <View> 
                    <Text>1つめのView</Text> 
                </View> 
                <View> 
                    <Text>2つめのView</Text> 
                </View> 
            </View> 
        ); 
    } 
} 


コンポーネントのpropsに指定するのは、関数の「定義」

Buttonコンポーネントがあるとします。

export default class App extends Component { 
    render() { 
        return ( 
            <View> 
                <Button 
                    title = "ボタン" 
                    onPress = /*ボタンが押されたときに実行される関数*/ 
                /> 
            </View> 
        ); 
    } 
} 
ここでonPressの部分に書くのは、
onPress = { Alert.alert("hoge") }

ではなく
onPress = { () => {Alert.alert("hoge")} }

です。関数形式にしないと、この場合ことあるごとにアラートがでて大変なことになります。


AsyncStorageからとってきたものは文字列

AsyncStorageというのは、端末内のストレージです。アプリの内容を保存しておくことで、アプリがリロードされたときも前の内容を引き続き表示することができます。

AsyncStorageから情報を取ってきていじることを考えます。

AsyncStorage.getItem('user1', (err, result) => { 
    value = result; 
}) 
console.log(value) // {"name":"taro","age":25} 
console.log(value.name) // undefined 
2つのログを比較するとなんで???って思うんですが、これ要はvalueはただの文字列なんですよね。2行目で

value = JSON.parse(result); 
としてやることで解決します。AsyncStorageからとってきたものは真っ先にJSON.parse()してやるのがよさそう。

逆に、AsyncStorageに保存するときにはJSON.stringify()をかけてやる必要がありますし、AsyncStorageには文字列を保存できるということは、こういうことができます(邪道かな?)

// 誕生日が来たらユーザー情報の年齢が加算されるようにしたい 
// {ユーザーID: {name: 名前, age: 以前+1}} 
newUserDataString = '{"' + users[userId].id + '":{"name":' + (users[userId].name) + ',"age":' + (users[userId].age + 1) + '}}'; 
// {"taro":{"name":"taro","age":26}} 
 
AsyncStorage.setItem(users, usersInfo, (err, result) => { 
    AsyncStorage.mergeItem(users, newUserDataString, (err, result) => { 
        AsyncStorage.getItem(users, (err, result) => { 
            usersObj = JSON.parse(result); 
            console.log(usersObj); 
        }); 
    }); 
}); 
なお、オブジェクトの要素にアクセスする際のブラケット記法を使うことで、要素名がころころ変わる場合でも対応できます。


render() は props / state が変更されるたびに実行される

ので、非同期処理を行っていたりして実行順が変わってしまった場合に、setState()で指定した変数が返ってくる前にsetState()が実行されるとエラーが起きたりします。

async/awaitなどについては完全に理解できているわけではないので、今後の課題とさせてください、、要は実行順がバグの温床になるよって話です。


子コンポーネントで親コンポーネントのstateを変更する

propsを使って関数を受け渡してやることで実現できます(ちょっとトリッキーじゃないですか?)。

class child extends Component { 
    render() { 
        return ( 
            <View> 
                <Button 
                    title = "change state" 
                    onPress = this.props.changeState 
                /> 
            </View> 
        ); 
    } 
} 
 
export default class parent extends Component { 
    // constructorは省略 
 
    changeStateFunc() { 
        this.setState(/*hoge*/); 
    } 
 
    render() { 
        return ( 
            <View> 
                <child 
                    changeState = { () => { this.changeStateFunc(); } } 
                /> 
            </View> 
        ); 
    } 
} 


future work

JavaScriptについてちゃんと学ぼうと思いました。具体的にはthisasync/awaitなど。

あとはSwiftも触ってみて、React Native との比較ができたらいいなと思います。

コメント

このブログの人気の投稿

投稿時間:2021-06-20 02:06:12 RSSフィード2021-06-20 02:00 分まとめ(3871件)

投稿時間:2021-04-30 23:37:32 RSSフィード2021-04-30 23:00 分まとめ(42件)

投稿時間:2023-02-05 02:09:04 RSSフィード2023-02-05 02:00 分まとめ(9件)