こんにちは!かたつむり(@Katatumuri_nyan)です!
Reactを触ってみて、サイト的なものは作れるようになりました(*´ω`)
そこで、次はReactNativeを触ってみようと思い、簡単なテキストエディタを作成しようと企んでおります(笑)
長い環境構築を経て、やっと前回ReactNativeを触ることができました✨
これで、アプリは作れそうだってことが分かったので、ここから最低限の機能を付けていきたいと思います~!
最初から見る↓
【1】React Nativeでテキストエディタを作ってみる!【下調べ編】
前回を見る↓
【5】React Nativeでテキストエディタを作ってみる!【テキストエリア作成編】
目次
ブランチを切る
作業ブランチがmasterのままなので、ブランチ切りましたw
今回、WSL2で環境構築した関係なのか、gitが管理者権限必要で、sourcetreeで操作できなくなっちゃいました💦
(環境構築の時に色々いじっておかしくなった可能性も)
ということで、WSL2のubuntuちゃんから、管理者権限でgitコマンド打ってます😨
何か方法あるとも思うけど、コマンド打った方が速いので←
(VScodeから手軽にコマンド打てるからひゃっほい)
データ管理
一応テキストエディタなので、データをどこかに格納しないといけません。
(ボタン作ってて気づきましたw)
今回は、アプリ開発が開始できるのか、ちゃんとコーディングできるのか、あんまり見通しが立ってなかったので、デザイン等していなくてw
データ管理について考えていませんでしたw
データ管理はどうやってするのか、ちょっと調べたいと思います。
端末に保存するケース
[React Native] データ永続化についてAsyncStorageとRealmを調べてみた
↑これを読む感じだと、AsyncStorage
で良い気がしますね。
以前、ReactでFirebaseを使ったことがありますが、クラウドでデータ管理するほどでもない気がしてて。
【ReactNative】AsyncStorageを使ってデータを保存・取得する方法
↑これがReact Nativeの元のAsyncStorage
っぽいですね。
react-native-storageでデータを保持する
↑簡単そうなので、今回はこちらをベースに実装していけそう。
クラウドに保存するケース
react-native-storageで端末にデータを持たせる
↑より
こんな場合にオススメ
ローカルにRDBを持つほどではないデータ(単一のフラグや日付、文字列など)
ぱっと思いつくのは、アプリの動作に関わる設定値ですかね。
例えばアプリの設定画面からテーマカラーを変更できて、そのテーマカラーはアプリ内でEnumで定義されている場合、端末内に保持していれば大筋問題ないはずです。
※上記の場合も、同一アプリを複数端末にインストールしていて、テーマカラーを共有したい場合等はサーバーサイドにデータを持たせておくべきだとは思います。
むむむ
同一アプリを複数端末にインストールしていて、テーマカラーを共有したい
したい!(`・ω・´)
テーマを実装するかしないかはおいておいて、メモデータの共有はしたいですね…。
React Nativeにおけるローカルデータベースの考察
↑すごい!
クラウドでのデータの同期にはアカウント作成が必要ですよね。
アカウント作成機能までつけるのはちょっとハードルが高いですね~。
今回は、共有機能を使ってクラウド(Googleドライブとか)に保存できるくらいにとどめたいと思います。
(バックアップではなく、単純に共有にします。)
React Native + Expo アプリでunstatedのデータを永続化
データの保存方法決定
- ローカルストレージに保存する
- 共有機能で外部ストレージにも保存できる
- アプリを閉じた時の未保存のデータはローカルストレージに保持
で行こうと思います!
データの読み書きをする
React Nativeで遊んでみよう – 其ノ弐:react native storage でデータを保存
↑を参考に実装していきます!
とりあえずnpm install react-native-storage
してExpo再起動
実装手順としては、以下のようにできたらなと。
- 保存・開くボタンを作る
- データを保存できるようにする
- データを開けるようにする
- データ一覧を取得する
- データ一覧から開けるようにする
よっしゃ!
1. 保存・開くボタンを作る
パネルの上にメニューバーを作って、そこに保存・開くボタンを作ります。
// panelコンポーネントの上の方 <View style={styles.panelBody}> <View style={styles.panelMenu}> <Pressable style={styles.button}><Text style={styles.buttonText}>開く</Text></Pressable> <Pressable style={styles.button}><Text style={styles.buttonText}>保存</Text></Pressable> </View> </View>
2. データを保存できるようにする
とりあえず保存していきます!w
React Nativeで遊んでみよう – 其ノ弐:react native storage でデータを保存
↑を参考に初期設定と、保存の設定をしました。
【ReactNative】現在の日付、時間表示をする方法
保存時間も設定
データの設定
export function fileData(filetitle, filetext) { const filename = filetitle + '.md' const date = new Date().toLocaleString() return ({ key: 'loginState', id: 'id', data: { name: filename, date: date, text: filetext } }) }
↑テストデータはこんな感じ
データの保存関数の作成
あれ!AsyncStorage
が非推奨だ!www
sunnylqm/react-native-storageによるとnpm install @react-native-community/async-storage
これも必要みたいです。
いれます!
- remove @react-native-community/async-storage from package.json - run "expo install @react-native-async-storage/async-storage" - update your imports manually, or run "npx expo-codemod sdk41-async-storage './**/*'".
expo install 使ってないからワーニングでちゃったw
package.json
から@react-native-community/async-storage
を消して、
expo install expo install @react-native-async-storage/async-storage
します!
importも右に変えます。import AsyncStorage from '@react-native-async-storage/async-storage'
新たにStorage.js
ってファイルを作って、とりあえず以下のコードにしました。
import AsyncStorage from '@react-native-async-storage/async-storage'; import Storage from 'react-native-storage'; //ストレージの設定 var storage = new Storage({ // 最大容量, 1000がデフォルト size: 1000, // AsyncStorageを使う(WEBでもRNでも)。 // セットしないとリロードでデータが消えるよ。 storageBackend: AsyncStorage, // (たぶん)キャッシュの期限。デフォルトは一日(1000 * 3600 * 24 milliseconds). // nullにも設定できて、期限なしの意味になるよ。 defaultExpires: 1000 * 3600 * 24, // メモリにキャッシュするかどうか。デフォルトは true。 enableCache: true, // リモートシンクの設定(だと思う。) sync: { // これについては後述 } }) export function fileData(filetitle, filetext) { const filename = filetitle + '.md' const date = new Date().toLocaleString() return ({ key: 'mdfile', id: 'id', data: { name: filename, date: date, text: filetext } }) } export function saveFileData(fileData) { storage.save(fileData); } export function loadFileData(fileData) { storage.load(fileData).then(ret => { // ロードに成功したら console.log(ret.name + ' is ' + ret.text); }).catch(err => { // ロードに失敗したら console.warn(err.message); switch (err.name) { case 'NotFoundError': // 見つかんなかった場合の処理を書こう break; case 'ExpiredError': // キャッシュ切れの場合の処理を書こう break; } }); }
テキストの冒頭行からファイル名を取得
さっきの関数を使ってみます。
先頭の行をファイル名にするようにします!
テキストを配列にして、空文字じゃない先頭の行を返すようにします。
// App.js function saveFileData(){ const firstRowEndPos = textInput.split('\n'); const filetitle = firstRowEndPos.filter(Boolean)[0] S.saveFileData(S.fileData(filetitle, textInput)) }
// App.js <MyPanel saveFileData={saveFileData} />
パネルの保存ボタンに渡してみました。
保存できたっぽいんですが、できているのかわかりませんw
3. データを開けるようにする
とりあえず、保存したものをロードしてみますw
開けている!!!
どこに保存しているんだろうw
後で保存しているところを突き止めますw
あと、保存できたかどうかのモーダルかポップアップも出したいな。
4. データ一覧を取得する
データ一覧の取得ってできるんかいな…?
getallkeysこれかな?
取得はできたっぽいですね。
// App.js コンポーネント function GetAllData() { const [data, setData] = useState('') useEffect(() => { S.GetAllData().then(e => { console.log('App.js 4>>'+e); }) }, []); console.log(data); return( <Text>a</Text> ) }
// Storage.js 情報取得関数 export async function loadFileData(fileData) { const data = await storage.load(fileData).then(ret => { return ret }).catch(err => { console.warn('S 54>>'+ JSON.stringify(fileData.key) + ">>>>" + err.message + ">>>>" +err); switch (err.name) { case 'NotFoundError': break; case 'ExpiredError': break; } }); return data } export async function GetAllData(){ let data = [] keys = await AsyncStorage.getAllKeys() for(let i in keys){ const key = keys[i]; const json = await loadFileData({ key: key }); if(!json===false){ data.push(await loadFileData({ key: key })) } } return data }
データ一覧を表示
こんな感じでデータを取得できたので、表示してみたいと思います(*´ω`)
お!表示できました。
今はTextコンポーネントで表示しているので、ボタンにしたりなんやかんやします。
あと、App.js
に書いているので、パネルコンポーネントの方に移植します。
パネルに移植できました(*´ω`)
色が絶賛やばいですが、何も考えないことにしましょうw
5. データ一覧から開けるようにする
つぎは、データ一覧のボタンをおしたら開くようにします。
親子コンポーネント間のデータの受け渡し難しい💦
とりあえず、key
でとってこれるみたいなので、データ名を押されたときにkey
を親コンポーネントにとばしたいと思います。
とりあえず、プレビューの方には反映できました!
テキストエリアにも反映したい🤔
できました~!
とりあえずこれで、データを保存して開くことはできますね。
データを編集したりするのはもうちょっと手順が必要そうです。
あと、開くボタンが必要なさそうなので←
新規作成ボタンに変えます。
回線エラー出てきた
Cannot connect to the Metro server. Try the following to fix the issue: - Ensure that the Metro server is running and available on the same network - Ensure that your device/emulator is connected to your machine and has USB debugging enabled - run 'adb devices' to see a list of connected devices - If you're on a physical device connected to the same machine, run 'adb reverse tcp:8081 tcp:8081' to forward requests from your device - If your device is on the same Wi-Fi network, set 'Debug server host & port for device' in 'Dev settings' to your machine's IP address and the port of the local dev server - e.g. 10.0.1.1:8081 URL: packager.mp-kiu.anonymous.reactnative-texteditor.exp.direct:80
手順に従って直してみます。
これでLAN接続できるといいな
adb reverse tcp:8081 tcp:8081
接続できなくなったww
パソコン再起動して事なきを得ましたw
LAN問題はまた今度にしよう。
データのkey設定
データにkeyを持たせようと思います。
データの固有のID的に使えるはず。
const [dataKey, setDataKey] = useState(null)
- 新規作成時→
dataKey=null
- データ編集時→
dataKey=固有のkey
となるようにします。
null
の時に、連番のkeyを発行するようにしよう!
keyには_
が使えないので、そこだけ気を付けていきます。
// App.js function saveFileData(){ const firstRowEndPos = textInput.split('\n'); const filetitle = firstRowEndPos.filter(Boolean)[0] let key = dataKey if (!key){ key = 'SimpleMD' + data.length + 1 } if (!textInput===false){ S.saveFileData(S.fileData(key, filetitle, textInput)) }else{ console.log('テキストが入力されていません'); } }
できました~!
保存後にデータ一覧に反映する
新規作成して保存した時に、パネルのデータ一覧に反映するようにしました。
// App.js function saveFileData(){ const firstRowEndPos = textInput.split('\n'); const filetitle = firstRowEndPos.filter(Boolean)[0] let key = dataKey if (!key){ key = 'SimpleMD' + data.length + 1 } if (!textInput===false){ S.saveFileData(S.fileData(key, filetitle, textInput)) }else{ console.log('テキストが入力されていません'); } getAllData() }
保存時にモーダルを出す
保存できたかどうか分かり難いので、モーダルを出そうと思います!
公式ドキュメントにモーダルコンポーネントがあるので、使っていきます。
アラートって言うコンポーネントもあるんですが、モーダルの方が可愛いので、モーダルにしますw
// App.js <Modal animationType={'slide'} presentationStyle={false} transparent={true} visible={isModalOpen} onRequestClose={()=>{ console.log('とじるんるん'); }} > <View style={styles.centeredView}> <View style={styles.modal}> <Text>{isSubmit?"保存できました!":"保存できませんでした!"}</Text> <Pressable style={styles.modalBtn} onPress={closeModal} > <Text style={styles.modalText}>閉じる</Text> </Pressable> </View> </View> </Modal>
React Nativeはコンポーネントが用意されているから楽ですね~(*´ω`)
今日はこの辺にしたいと思います!
続きをお楽しみに(*´ω`)
↓続き
【7】React Nativeでテキストエディタを作ってみる!【エクスポート・バックアップ編】