React Native connection with SQLite database - javascript

I'm new with React Native and i would like to know if I can connect with a local SQLite database file (.db) i followed some guide on Github but it doesn't work for me, is there any other way to do it?? (using Android)
Here is the guide I said: https://github.com/andpor/react-native-sqlite-storage

global.db = SQLite.openDatabase(
{
name: "silkyMarket",
location: "default",
createFromLocation: "~SQLite.db",
},
() => {},
(error) => {
console.log("ERROR: " + error);
}
);
function SettingsScreen() {
return (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<Text>Settings!</Text>
</View>
);
}
add that to your app's entry file like app.js of index.js then you can access it in other files like this:
import React from "react";
import SQLite from "react-native-sqlite-storage";
export default class SQLiteScreen extends React.Component {
constructor() {
super();
SQLite.DEBUG = true;
}
ExecuteQuery = (sql, params = []) =>
new Promise((resolve, reject) => {
db.transaction((trans) => {
trans.executeSql(
sql,
params,
(trans, results) => {
resolve(results);
},
(error) => {
reject(error);
}
);
});
});
// Create Table
async CreateTable() {
let Table = await this.executeQuery(
"CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY NOT NULL, first_name VARCHAR(16), last_name VARCHAR(16), is_deleted INTEGER)",
[]
);
console.log(Table);
}
}

Related

TypeORM query returns an empty array

I'm trying to retrieve data from an existing database. I recently switched to bare workflow from Expo managed, trying to test react-native-sqlite-storage with TypeORM. However, TypeORM raw query keeps returning an empty array and I have no idea why.
Here's my code and it is based on this example https://dev.to/jgabriel1/expo-sqlite-typeorm-4mn8
DictionaryScreen.js
function DictionaryScreen(props) {
const { ejRepository } = useDatabaseConnection();
const handleinputWord = async () => {
console.log(props.inputWord); //=>this console.logs the text from the input below
try {
const results = await ejRepository.getWordandMean(props.inputWord);
console.log(results); //=> Here I expect an array of the query result but it only returns []
} catch (error) {
console.log(error)
}
}
return(
<View style={styles.searchContainer}>
<MaterialCommunityIcons name="text-search" color="white" size={30}/>
<TextInput
style={styles.input}
onChangeText={props.updateTextInput}
onSubmitEditing={handleinputWord}
value={props.inputWord}
placeholder='Look up the word (Type exactly)'
keyboardType="default"
/>
<StatusBar style='light' />
</View>
)
}
const mapStateToProps = state => {
return {
inputWord: state.inputWord
}
};
const mapDispatchToProps = {updateTextInput};
export default connect(mapStateToProps, mapDispatchToProps) (DictionaryScreen);
repository.ts
export class EJRepository {
private ormRepository: Repository<EJModel>;
constructor(connection: Connection) {
this.ormRepository = connection.getRepository(EJModel);
}
public async getWordandMean(props: any): Promise<EJModel> {
console.log(props); //=> this returns the same text from DictionaryScreen
const results = await this.ormRepository.query(
`SELECT * FROM ejmodel WHERE word LIKE '%${props}%';`, [props]);
return results;
}
}
connection.tsx
interface DatabaseConnectionContextData {
ejRepository: EJRepository;
}
const DatabaseConnectionContext = createContext<DatabaseConnectionContextData>(
{} as DatabaseConnectionContextData,
);
export const DatabaseConnectionProvider: React.FC = ({ children }) => {
   const [connection, setConnection] = useState<Connection | null>(null);
   const connect = useCallback(async () => {
 const createdConnection = await createConnection({
  type: 'react-native',
  name: "ejdict.v1.0",
  database: '***.db',
  entities: [EJModel],
location: 'default',
   migrations: [ejdict1621603544180],
  migrationsRun: true,
  synchronize: false,
logging: true,
  extra: {createFromLocation: '/Users/***/***/ios/***/www/***.db' }
 });
 setConnection(createdConnection);
 }, []);
useEffect(() => {
if (!connection) {
connect();
}
}, [connect, connection]);
if (!connection) {
return <ActivityIndicator />;
}
return (
 <DatabaseConnectionContext.Provider
value={{
ejRepository: new EJRepository(connection),
}}
>
{children}
</DatabaseConnectionContext.Provider>
);
};
export function useDatabaseConnection() {
const context = useContext(DatabaseConnectionContext);
return context;
}
EJModel.ts
import {Entity, PrimaryColumn, Column} from "typeorm";
#Entity({name: "ejmodel"})
export class EJModel {
#PrimaryColumn({type: 'integer'})
item_id: number;
#Column({type: 'text'})
word: string;
#Column({type: 'text'})
mean: string;
#Column({type: 'integer'})
level: number;
}
PS: Is it an unusual attempt to load an existing database on a React Native project? Working examples are almost no existent. I once made it work on Expo, but I realised I needed migration functionality and tried to do the same thing with TypeORM wasting lots of time...
I have found the solution for this problem by following step
adjust the entity of the table to
#Entity({
name: YOUR_TABLE_NAME,
synchronize: false,
})
Put the existed database into dir
android/app/src/main/assets or android/app/src/main/assets/www
Typeorm declare
export const AppDataSource = new DataSource({
type: "react-native",
database: database.db,
location: "default",
synchronize: true,
entities: [YOUR_ENTITY],
logging: ["error", "query", "schema"],
});
//note that can set to false if you want all tables to not synchronize or can set individually in each Entity like step 1.
4) In your index.android.js or App.js file, openDatabase to get connect to the database.
SQLite.openDatabase(
{
name: "database.db",
createFromLocation: "~database23_02_17.db",
location: "default",
},
() => {
console.log("db connection success");
},
() => {
console.log("db connection error");
}
);
Uninstall the app if you've already installed in your physical device (because the app will not update the database file) and then run build again
cd android &&./gradlew clean
cd .. && react-native run-android

I got navigation as undefined in react navigation 5?

I have a reusable component for Sign in with Apple Button
After user success, i navigate hem to Home screen
But i notes when i log navigation it's log undefined,
and when i log this.props i just got the two actions i made in redux!
So how can i access to navigation in this component and why it's not accessed by default!
Log
props => {"isLogin": [Function isLogin], "storeToken": [Function storeToken]}
navigation => undefined
Code
import appleAuth, {
AppleAuthCredentialState,
AppleAuthError,
AppleAuthRealUserStatus,
AppleAuthRequestOperation,
AppleAuthRequestScope,
AppleButton,
} from '#invertase/react-native-apple-authentication';
import React from 'react';
import {ActivityIndicator, StyleSheet, View} from 'react-native';
import {connect} from 'react-redux';
import API from '../../api/API';
import {isLoginFunc} from '../../redux/actions/isLoginAction';
import {saveToken} from '../../redux/actions/saveTokenAction';
class AppleAuth extends React.Component {
constructor(props) {
super(props);
this.authCredentialListener = null;
this.user = null;
this.state = {
credentialStateForUser: -1,
loading: false,
};
}
componentDidMount() {
const {navigation} = this.props;
console.log('did-navigation', navigation);
console.log('did- this.props', this.props);
/**
* subscribe to credential updates.This returns a function which can be used to remove the event listener
* when the component unmounts.
*/
this.authCredentialListener = appleAuth.onCredentialRevoked(async () => {
// console.warn('Credential Revoked');
this.fetchAndUpdateCredentialState().catch(error =>
this.setState({credentialStateForUser: `Error: ${error.code}`}),
);
});
this.fetchAndUpdateCredentialState()
.then(res => this.setState({credentialStateForUser: res}))
.catch(error =>
this.setState({credentialStateForUser: `Error: ${error.code}`}),
);
}
componentWillUnmount() {
/**
* cleans up event listener
*/
this.authCredentialListener();
}
signIn = async () => {
// start a login request
try {
const appleAuthRequestResponse = await appleAuth.performRequest({
requestedOperation: AppleAuthRequestOperation.LOGIN,
requestedScopes: [
AppleAuthRequestScope.EMAIL,
AppleAuthRequestScope.FULL_NAME,
],
});
this.setState({loading: true});
const {
user: newUser,
email,
nonce,
fullName: {familyName, givenName},
identityToken,
realUserStatus /* etc */,
} = appleAuthRequestResponse;
let username = `${givenName} ${familyName}`;
this.user = newUser;
this.fetchAndUpdateCredentialState()
.then(res => {
this.setState({credentialStateForUser: res});
console.log('res:::', res);
})
.catch(error => {
console.log(`Error: ${error.code}`);
this.setState({credentialStateForUser: `Error: ${error.code}`});
});
if (identityToken) {
console.log('email', email);
console.log('username', username);
console.log('nonce', nonce);
this.sendData(email, username, nonce);
// e.g. sign in with Firebase Auth using `nonce` & `identityToken`
} else {
// no token - failed sign-in?
}
if (realUserStatus === AppleAuthRealUserStatus.LIKELY_REAL) {
console.log("I'm a real person!");
}
// console.warn(`Apple Authentication Completed, ${this.user}, ${email}`);
} catch (error) {
if (error.code === AppleAuthError.CANCELED) {
alert('User canceled Apple Sign in');
// console.warn('User canceled Apple Sign in.');
} else {
console.error(error);
}
}
};
fetchAndUpdateCredentialState = async () => {
if (this.user === null) {
this.setState({credentialStateForUser: 'N/A'});
} else {
const credentialState = await appleAuth.getCredentialStateForUser(
this.user,
);
if (credentialState === AppleAuthCredentialState.AUTHORIZED) {
this.setState({credentialStateForUser: 'AUTHORIZED'});
} else {
this.setState({credentialStateForUser: credentialState});
}
}
};
// Send data "name,image,email" to API
sendData = async (Email, Name, Id) => {
try {
let response = await API.post('/apple', {
email: Email,
name: Name,
id: Id,
});
let {
data: {
data: {
response: {token},
},
},
} = response;
console.log('token:?>:', token);
console.log('props', this.props);
console.log('navigation', this.props.navigation);
this.setState({loading: false});
this.props.storeToken(token);
this.props.isLogin(true);
// this.props.navigation.push('BottomTabNavigator');
} catch (err) {
console.log(err);
alert('Unexpected Error, try again later.');
this.setState({loading: false});
}
};
render() {
return (
<View style={styles.container}>
{this.state.loading ? (
<ActivityIndicator />
) : (
<AppleButton
style={styles.appleButton}
cornerRadius={5}
buttonStyle={AppleButton.Style.WHITE}
buttonType={AppleButton.Type.SIGN_IN}
onPress={() => this.signIn()}
/>
)}
</View>
);
}
}
const styles = StyleSheet.create({
appleButton: {
width: 200,
height: 50,
// margin: 10,
},
container: {
flex: 1,
justifyContent: 'center',
},
});
const mapDispatchToProps = dispatch => {
// to excute the actions we want to invok
return {
isLogin: isLogin => {
dispatch(isLoginFunc(isLogin));
},
storeToken: token => {
dispatch(saveToken(token));
},
};
};
export default connect(
null,
mapDispatchToProps,
)(AppleAuth);
-
singin.js
<AppleAuth /> in the render method
if you render your component as component, not as a navigation screen, it will not receive navigation prop. It was like this in all versions of react-navigation
Access the navigation prop from any component

Monitoring Services And Characteristics on React-Native

I have two questions for you about my code :)
I'm creating an android to connect to ble device. I'm using ble-plx library.
import React, {Component} from 'react';
import { Platform, View, Text } from 'react-native';
import { BleManager } from 'react-native-ble-plx';
export default class Main extends Component {
constructor() {
super();
this.manager = new BleManager()
this.state = {info: "", values: {}}
this.deviceprefix = "Device";
this.devicesuffix_dx = "DX";
this.sensors = {
"0000f0fd-0001-0008-0000-0805f9b34fb0" : "Acc+Gyr+Mg",
"0000f0f4-0000-1000-8000-00805f9b34fb" : "Pressure"
}
}
serviceUUID() {
return "0000f0f0-0000-1000-8000-00805f9b34fb"
}
notifyUUID(num) {
return num
}
model_dx (model) {
return this.deviceprefix + model + this.devicesuffix_dx
}
info(message) {
this.setState({info: message})
}
error(message) {
this.setState({info: "ERROR: " + message})
}
updateValue(key, value) {
this.setState({values: {...this.state.values, [key]: value}})
}
componentWillMount(){
const subscription = this.manager.onStateChange((state) => {
if (state === 'PoweredOn') {
this.scanAndConnect();
subscription.remove();
}
}, true);
}
async requestPermission() {
try {
const granted = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION);
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
this.setState({permissionStatus:'granted'});
}else if(granted === PermissionsAndroid.RESULTS.DENIED) {
this.setState({permissionStatus:'denied'});
}else{
}
} catch (err) {
console.error(err)
}
}
scanAndConnect(){
this.manager.startDeviceScan(null, null, (error, device) => {
if (error) {
return
}
let model = '0030';
if (device.name == this.model_dx(model) ) {
this.manager.stopDeviceScan();
device.connect()
.then((device) => {
this.info("Discovering services and characteristics ")
return device.discoverAllServicesAndCharacteristics()
})
.then((device) => {
this.info("SetupNotification")
return this.setupNotifications(device)
})
.catch((error) => {
this.error(error.message)
});
}
});
}
async setupNotifications(device) {
for (const id in this.sensors) {
const service = this.serviceUUID(id)
const characteristicN = this.notifyUUID(id)
device.monitorCharacteristicForService(service, characteristicN, (error, characteristic) => {
if (error) {
this.error(error.message)
return
}
this.updateValue(characteristic.uuid, characteristic.value)
})
}
}
render() {
return (
<View>
<Text>{this.state.info}</Text>
{Object.keys(this.sensors).map((key) => {
return <Text key={key}>
{this.sensors[key] + ": " + (this.state.values[this.notifyUUID(key)] || "-")}
</Text>
})}
</View>
)
}
}
I have two question for you:
1) The monitoring give me values like:
For example in the case of "Pressure" the value: "ZABIAGYAZwBoAA=="
I should receive a number not string, I have tried to convert Text into Binary and then Binary to Decimal. How can I do, in your opinion in the app?
2)
I print the values using:
<Text>{this.state.info}</Text>
{Object.keys(this.sensors).map((key) => {
return <Text key={key}>
{this.sensors[key] + ": " + (this.state.values[this.notifyUUID(key)] || "-")}
</Text>
But If I wanted to access the "pressure" value directly, how could I do it? To be clear if I wanted to, for example, passing the value that remembers me pressure on another page?
Thank you all for the support!
install this library:
npm install --save base-64
import it in your Main class:
import base64 from 'base-64'
then in your updateValue method:
updateValue(key, value) {
const readebleData = base64.decode(value);
this.setState({values: {...this.state.values, [key]: readebleData}})
}
If you want to pass the readableData to another class you should use library like react-redux or passing the variable during the transition to the new class, for example using react-native-router-flux:
Actions.pageKey({ data: readebleData })

Sending status updates through helper function in react native

I have the following screen where I call a helper function pouchDB_helper.sync() to gather a bunch of data for me.
The goal is to be able to record where in the function it is currently at so I can give a percent or a status update in my render()
I'm new to react / react-native so I'm not sure if this is the right way to go about doing it. I'd like to be able to keep it as a helper function if possible because I use this function in other areas, this is just the only place I actually need a status update on where it's at in the process.
import React, { Component } from 'react';
import { ActivityIndicator, AsyncStorage, Button, StatusBar, Text, StyleSheet, View, } from 'react-native';
import * as pouchDB_helper from '../utils/pouchdb';
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'flex-start',
justifyContent: 'center',
padding: "5%",
backgroundColor: "#fff",
width:"100%"
},
statusHeader: {
fontSize: 18,
fontWeight: "600",
marginBottom: 10,
textAlign:'center',
width:'100%'
}
});
type Props = {};
export default class SyncScreen extends Component<Props> {
static navigationOptions = {
title: 'Syncing Settings',
};
render() {
pouchDB_helper.sync().then((response) => {
//IT'S DONE
}, (error) => { alert("THERE WAS AN ERROR"); });
return (
<View style={styles.container}>
<Text style={styles.statusHeader}>Syncing, please wait..</Text>
<Text>WHERE I WANT TO CHANGE TEXT</Text>
</View>
);
}
}
pouchDB_helper example
note: This is just an example. I know the .get() won't take long enough to warrant a status but I'm just trying to understand the concept.
import React from 'react';
import { AsyncStorage } from 'react-native';
import PouchDB from 'pouchdb-react-native'
export async function sync() {
const company_id = await AsyncStorage.getItem('company_id');
const device_db = new PouchDB(company_id, {auto_compaction: true});
//STATUS UPDATE 1
return device_db.get("settings").then((s) => {
//STATUS UPDATE 2
return device_db.get("get_this").then((s) => {
//STATUS UPDATE 3
return device_db.get("get_that").then((s) => {
//STATUS UPDATE 4
}, (error) => { return false; });
}, (error) => { return false; });
}, (error) => { return false; });
}
Simple approach would be passing a function to the sync function which can change the state and set the desired text on component.
Example
constructor(props) {
super(props);
this.state = {
level: 'some init value'
};
}
onChangeState = (level) => {
this.setState({level});
}
componentDidMount() {
pouchDB_helper.sync(this.onChangeState).then((response) => {
//IT'S DONE
this.onChangeState('Finished');
}, (error) => { alert("THERE WAS AN ERROR"); });
}
render() {
return (
<View style={styles.container}>
<Text style={styles.statusHeader}>Syncing, please wait..</Text>
<Text>{`Current level is ${this.state.level}`}</Text>
</View>
);
}
export async function sync(changeState) {
const company_id = await AsyncStorage.getItem('company_id');
const device_db = new PouchDB(company_id, {auto_compaction: true});
//STATUS UPDATE 1
changeState(1);
return device_db.get("settings").then((s) => {
//STATUS UPDATE 2
changeState(2);
return device_db.get("get_this").then((s) => {
//STATUS UPDATE 3
changeState(3);
return device_db.get("get_that").then((s) => {
//STATUS UPDATE 4
changeState(4);
}, (error) => { return false; });
}, (error) => { return false; });
}, (error) => { return false; });
}

Unable to display result of spotify api query in a FlatList

I'm working on a very simple react-native app where I type the name of an artist in a search bar and display a Flatlist of artists that I got using the spotify api.
I have 2 files my App.js that does the rendering and fetcher.js that implements the api calls.
But I'm unable to get the list to appear, I'm unable to set the state of artists.
App.js
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
FlatList,
StatusBar,
TextInput,
} from 'react-native';
import colors from './utils/colors';
import { List, ListItem, SearchBar } from 'react-native-elements';
import { searchArtist } from './utils/fetcher';
import { debounce } from 'lodash';
export default class spotilist extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
data: [],
query: '',
artists: [],
error: null,
refreshing: false,
};
}
render() {
return (
<View style={ styles.container }>
<StatusBar barStyle="light-content" />
<TextInput style={ styles.searchBox }
value={this.state.value}
onChangeText={ this.makeQuery }
/>
<Text> {this.state.artists} </Text>
</View>
);
}
makeQuery = debounce(query => {
searchArtist(query)
.then((artists) => {
this.setState({
artists: this.state.artists,
});
//console.log(artists)
})
.catch((error) => {
throw error;
});
}, 400);
}
const styles = StyleSheet.create({
container: {
paddingTop: 64,
flex: 1,
backgroundColor: colors.white,
},
searchBox: {
height: 40,
borderColor: colors.black,
borderWidth: 2,
margin: 16,
paddingLeft: 10,
fontWeight: '800',
},
row: {
flex: 1,
margin: 30,
alignSelf: 'stretch',
justifyContent: 'center',
},
});
fetch.js
export function searchArtist(query) {
const ClientOAuth2 = require('client-oauth2')
console.log("Query : " + query)
const spotifyAuth = new ClientOAuth2({
clientId: CLIENT_ID,
clientSecret: CLIENT_SECRET,
accessTokenUri: 'https://accounts.spotify.com/api/token',
authorizationUri: 'https://accounts.spotify.com/authorize',
scopes: []
})
spotifyAuth.credentials.getToken()
.then((user) => user.accessToken)
.then((token) => getQuery(token, query))
.then((result) => {
console.log(result) // No list :(
return result
});
}
function getQuery(token, query) {
console.log("Query2 : " + query)
const settings = {
"url": `https://api.spotify.com/v1/search?q=${ query }&type=artist`,
"method": "GET",
"headers": {
"authorization": "Bearer " + token,
"cache-control": "no-cache",
}
}
fetch(settings)
.then((res) => res.json())
.then(data => {
const artists = data.artists ? data.artists.items : [];
console.log(artists) // I get the list in the debbuger
return artists;
});
}
Thank you for your help.
You just need to return you fetch promise in getQuery
function getQuery(token, query) {
console.log("Query2 : " + query)
const settings = {
"url": `https://api.spotify.com/v1/search?q=${ query }&type=artist`,
"method": "GET",
"headers": {
"authorization": "Bearer " + token,
"cache-control": "no-cache",
}
}
return fetch(settings)
.then((res) => res.json());
}
And then when you call
spotifyAuth.credentials.getToken()
.then((user) => user.accessToken)
.then((token) => getQuery(token, query))
.then((result) => {
console.log(result) // No list :(
return result
});
getQuery will return this promise and you can handle it like you did before in getQuery:
return spotifyAuth.credentials.getToken()
.then((user) => user.accessToken)
.then((token) => getQuery(token, query))
.then(data => {
return data.artists ? data.artists.items : [];
});
then you can simple return this promise and handle wherever you want
You need to map through the array of artists. All react and react-native components cannot render data outside of data primitives (such as strings and numbers).
Such as:
{
this.state.artists.map(artist => {
return (
<Text key={artist.id}>{artist.name}</Text>
)
})
}
If the elements inside the state.artists array are just strings, just return the artist inside the text element.
The key value is for React to quickly assimilate the virtual dom to dom amidst state changes.

Categories

Resources