こんにちは!かたつむり(@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】現在の日付、時間表示をする方法
保存時間も設定
データの設定
xxxxxxxxxx
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
これも必要みたいです。
いれます!
xxxxxxxxxx
- 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
ってファイルを作って、とりあえず以下のコードにしました。
xxxxxxxxxx
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;
}
});
}
テキストの冒頭行からファイル名を取得
さっきの関数を使ってみます。
先頭の行をファイル名にするようにします!
テキストを配列にして、空文字じゃない先頭の行を返すようにします。
xxxxxxxxxx
// App.js
function saveFileData(){
const firstRowEndPos = textInput.split('\n');
const filetitle = firstRowEndPos.filter(Boolean)[0]
S.saveFileData(S.fileData(filetitle, textInput))
}
xxxxxxxxxx
// App.js
<MyPanel
saveFileData={saveFileData}
/>
パネルの保存ボタンに渡してみました。
保存できたっぽいんですが、できているのかわかりませんw
3. データを開けるようにする
とりあえず、保存したものをロードしてみますw
開けている!!!
どこに保存しているんだろうw
後で保存しているところを突き止めますw
あと、保存できたかどうかのモーダルかポップアップも出したいな。
4. データ一覧を取得する
データ一覧の取得ってできるんかいな…?
getallkeysこれかな?
取得はできたっぽいですね。
xxxxxxxxxx
// 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>
)
}
xxxxxxxxxx
// 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
を親コンポーネントにとばしたいと思います。
とりあえず、プレビューの方には反映できました!
テキストエリアにも反映したい
できました~!
とりあえずこれで、データを保存して開くことはできますね。
データを編集したりするのはもうちょっと手順が必要そうです。
あと、開くボタンが必要なさそうなので←
新規作成ボタンに変えます。
回線エラー出てきた
xxxxxxxxxx
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的に使えるはず。
xxxxxxxxxx
const [dataKey, setDataKey] = useState(null)
- 新規作成時→
dataKey=null
- データ編集時→
dataKey=固有のkey
となるようにします。
null
の時に、連番のkeyを発行するようにしよう!
keyには_
が使えないので、そこだけ気を付けていきます。
xxxxxxxxxx
// 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('テキストが入力されていません');
}
}
できました~!
保存後にデータ一覧に反映する
新規作成して保存した時に、パネルのデータ一覧に反映するようにしました。
xxxxxxxxxx
// 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
xxxxxxxxxx
// 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でテキストエディタを作ってみる!【エクスポート・バックアップ編】