first interactions with db (state cache)

now we're cookin' with custom hooks
This commit is contained in:
austinried
2021-06-17 09:25:02 +09:00
parent 6fb4b30294
commit 2a6821c25d
6 changed files with 221 additions and 19 deletions

View File

@@ -0,0 +1,53 @@
import React from 'react';
import { Button, FlatList, Text, View } from 'react-native';
import { useRecoilValue, useResetRecoilState } from 'recoil';
import { artistsState, ArtistState, useUpdateArtists } from '../state/artists';
const ArtistItem: React.FC<{ item: ArtistState } > = ({ item }) => (
<View>
<Text>{item.id}</Text>
<Text style={{
fontSize: 60,
paddingBottom: 400,
}}>{item.name}</Text>
</View>
);
const List = () => {
const artists = useRecoilValue(artistsState);
const renderItem: React.FC<{ item: ArtistState }> = ({ item }) => (
<ArtistItem item={item} />
);
return (
<FlatList
data={artists}
renderItem={renderItem}
keyExtractor={item => item.id}
/>
);
}
const ArtistsList = () => {
const resetArtists = useResetRecoilState(artistsState);
const updateArtists = useUpdateArtists();
return (
<View>
<Button
title='Reset to default'
onPress={resetArtists}
/>
<Button
title='Update from API'
onPress={updateArtists}
/>
<React.Suspense fallback={<Text>Loading...</Text>}>
<List />
</React.Suspense>
</View>
);
}
export default ArtistsList;

67
src/db/client.ts Normal file
View File

@@ -0,0 +1,67 @@
import SQLite, { DatabaseParams, ResultSet, SQLiteDatabase } from 'react-native-sqlite-storage';
SQLite.enablePromise(true);
abstract class DbStorage {
private dbParams: DatabaseParams;
constructor(dbParams: DatabaseParams) {
this.dbParams = dbParams;
}
abstract createDb(): Promise<void>
private async openDb(): Promise<SQLiteDatabase> {
return await SQLite.openDatabase({ ...this.dbParams });
}
async deleteDb(): Promise<void> {
await SQLite.deleteDatabase({ ...this.dbParams });
}
async executeSql(sql: string, params?: any[]): Promise<[ResultSet]> {
const db = await this.openDb();
try {
// https://github.com/andpor/react-native-sqlite-storage/issues/410
// if (params) {
// for (const p of params) {
// if (Array.isArray(p)) {
// throw new Error('param value cannot be an array');
// }
// }
// }
return await db.executeSql(sql, params);
} catch (err) {
try { await db.close(); } catch {}
throw err;
} finally {
try { await db.close(); } catch {}
}
}
}
export class MusicDb extends DbStorage {
constructor() {
super({ name: 'music.db', location: 'default' });
}
async createDb(): Promise<void> {
await this.executeSql(`
CREATE TABLE artists (
id PRIMARY KEY NOT NULL,
name NOT NULL,
starred
);
`);
}
}
class SettingsDb extends DbStorage {
constructor() {
super({ name: 'settings.db', location: 'Library' });
}
async createDb(): Promise<void> {
return;
}
}

65
src/state/artists.ts Normal file
View File

@@ -0,0 +1,65 @@
import md5 from 'md5';
import { atom, selector, useSetRecoilState } from 'recoil';
import { SubsonicApiClient } from '../subsonic/client';
import { MusicDb } from '../db/client';
const db = new MusicDb();
const password = 'test';
const salt = 'salty';
const token = md5(password + salt);
const client = new SubsonicApiClient('http://navidrome.home', 'austin', token, salt);
export interface ArtistState {
id: string;
name: string;
}
export const artistsState = atom<ArtistState[]>({
key: 'artistsState',
default: selector({
key: 'artistsState/default',
get: async () => {
await prepopulate();
const results = await db.executeSql(`
SELECT * FROM artists;
`);
return results[0].rows.raw().map(i => ({
id: i.id,
name: i.name,
}));
},
}),
});
export const useUpdateArtists = () => {
const setArtists = useSetRecoilState(artistsState);
return async () => {
const response = await client.getArtists();
setArtists(response.data.artists.map(i => ({
id: i.id,
name: i.name,
})));
};
};
async function prepopulate() {
try { await db.deleteDb() } catch {}
await db.createDb();
await db.executeSql(`
INSERT INTO artists (id, name, starred)
VALUES (?, ?, ?);
`,
[1, 'good guy', true]
);
await db.executeSql(`
INSERT INTO artists (id, name, starred)
VALUES (?, ?, ?);
`,
[2, 'bad guy', false]
);
}