I am trying to make stores which can be accessed by Screens. File structure:
index.ios.js
/app/index.js
/app/store/database.js
/app/store/userStore.js
index.ios.js :
import { AppRegistry } from 'react-native';
import App from './app/index';
AppRegistry.registerComponent('AwesomeProject', () => App);
/app/index.js :
import React, { Component} from 'react';
import {
Text,
View,
} from 'react-native';
import userStore from './store/userStore';
import ViewBackground from './components/ViewBackground';
class App extends Component {
constructor(props){
super(props);
this.isLoggedIn = true;
}
componentDidMount() {
this.fetchUsers().done();
}
async fetchUsers(){
console.log('Before User Fetch');
var result = await userStore.getAllUsers();
console.log('After User Fetch');
}
render() {
if(this.isLoggedIn){
return this.loggedInView();
}else{
return this.loggedOutView();
}
}
loggedInView(){
return <ViewBackground>
</ViewBackground>
}
loggedOutView(){
return <View>
<Text>Hi, This is logged Out view </Text>
</View>
}
}
export default App;
/app/store/userStore.js :
import React from 'react';
import database from './database';
class UserStore{
async getAllUsers(){
let query = {
type: 'SELECT',
sql: 'SELECT * FROM Users',
vals: [],
}
let queries = [query];
console.log('in store before');
let dbResult = await database.runQuery(queries);
console.log('in store after');
return dbResult;
}
}
const userStore = new UserStore;
export default userStore;
/app/store/database.js :
'use strict';
import React from 'react';
import SQLite from 'react-native-sqlite-storage';
SQLite.enablePromise(true);
var database_name = "test.db";
var database_version = "1.0";
var database_displayname = "Test";
var database_size = 1024 * 1024 * 10;
class Database {
constructor(){
SQLite.openDatabase(database_name, database_version, database_displayname, database_size).then(this.dbOpenSuccess).catch(this.dbOpenError);
}
dbOpenSuccess(dbInstance){
this.conn = dbInstance;
}
dbOpenError(err) {
}
getConnection() {
return this.conn;
}
setUpDatabase(){
let queries = [
'CREATE TABLE IF NOT EXISTS Users (user_id INTEGER PRIMARY KEY, f_name VARCHAR(64), l_name VARCHAR(64), email_id VARCHAR(128), mobile VARCHAR(10))'
];
this.conn.transaction( tx => {
queries.map( q => {
tx.executeSql(q, [], res => {}, err => {});
});
});
}
async runQuery(queries) {
await this.conn.transaction(tx => {
return Promise.all(queries.map(async (q) => {
try {
let results = null;
switch(q.type){
case 'SELECT':
results = await tx.executeSql(q.sql, q.vals);
console.log('Query', q, 'Executed. results:', results);
break;
}
} catch(err) {
console.log('Something went wrong while executing query', q, 'error is', err);
}
}));
});
return false;
}
closeDatabase(){
this.conn.close().then(this.dbCloseSuccess).catch(this.dbCloseError);
}
dbCloseSuccess(res) {
}
dbCloseError(err){
}
}
const dbInstance = new Database();
dbInstance.setUpDatabase();
export default dbInstance;
In /app/index.js I am trying to fetch users from userStore.js which will call database.js to retrieve the users from database. I want to pause execution when I fetch users from database, so tried async/await. I used console.log() to check whether execution paused. None of async/await is working. I am getting response after all console.log is done.
I want it to wait until I receive data from database.
You need to wrap it in a Promise, then resolve the result when sql execution is done.
for example
async () => {
return new Promise((resolve, reject) => {
db.transaction(tx => {
tx.executeSql('SELECT * FROM users;', [], (tx, results) => {
const { rows } = results;
let users = [];
for (let i = 0; i < rows.length; i++) {
users.push({
...rows.item(i),
});
}
resolve(users);
});
});
});
}
let SQLite = require('react-native-sqlite-storage');
const DB = SQLite.openDatabase({name:'test.db',createFromLocation:'~sqlitedb.db'});
class Database{
db;
constructor(db){
this.db =db;
}
executeSql = (sql,params=[])=>new Promise((resolve , reject)=>{
this.db.transaction((trans)=>{
trans.executeSql(sql,params,(db,results)=>{
resolve(results);
},
(error)=>{
reject(error);
});
});
});
}
export default (new Database(DB));
use
import Database from 'database';
try
{
results = await DB.excuteSql("SQL",['params']);
}
catch(e)
{
}
The following worked for me:
import { openDatabase } from 'react-native-sqlite-storage'
const db = openDatabase({
name: 'app.db',
location: 'default',
})
export default function App() {
useEffect(() => {
db.then((db) => {
db.transaction((tx) => {
tx.executeSql('SELECT this from that', [])
})
})
/* Rest of the component... */
}
openDatabase returns a Promise, on resolving which we get the actual db on which we can run db.transaction().
You could use the async/await syntax too, but that would be a lot of encapsulation, and so .then() is always cleaner.
This is different from the "expo-sqlite" package, because db = openDatabase('app.db') does not return a Promise there. Instead, you can run db.transaction() directly.
just add this lines
import {enablePromise, openDatabase } from 'react-native-sqlite-storage';
enablePromise(true)
I follow this way to do DB operations
export const fetchUsers = async () => {
return new Promise((resolve, reject) => {
db.transaction(txn => {
txn.executeSql('SELECT * FROM "Users";', [], (tx, res) => (
resolve(res.rows.raw())
))
})
})
}
rows.raw() will return the data as object
(async()=>{
data= await fetchUsers()
})()
Related
i am initializing a node js app with crucial data for the app to work from a database in index.js.
index.ts
import {getInitialData} from 'initData.ts';
export let APP_DATA: AppData;
export const initializeAppData = async () => {
try {
APP_DATA = (await getInitialData()) as AppData;
if (process.env.NODE_ENV !== 'test') {
initializeMongoose();
startServer();
}
} catch (error) {
console.log(error);
}
};
initData.ts
let dbName: string = 'initialData';
if (process.env.NODE_ENV === 'test') {
dbName = 'testDb';
}
const uri = `${process.env.MONGODB_URI}/?maxPoolSize=20&w=majority`;
export async function getInitialData() {
const client = new MongoClient(uri);
try {
await client.connect();
const database = client.db(dbName);
const configCursor = database
.collection('config')
.find({}, { projection: { _id: 0 } });
const config = await configCursor.toArray();
const aaoCursor = database
.collection('aao')
.find({}, { projection: { _id: 0 } });
const aao = await aaoCursor.toArray();
return { config, aao };
} catch {
(err: Error) => console.log(err);
} finally {
await client.close();
}
}
I'm using this array in another file and import it there.
missionCreateHandler
import { APP_DATA } from '../index';
export const addMissionResources = (
alarmKeyword: AlarmKeyword,
newMission: MissionDocument
) => {
const alarmKeywordObject = APP_DATA?.aao.find(
(el) => Object.keys(el)[0] === alarmKeyword
);
const resourceCommand = Object.values(alarmKeywordObject!);
resourceCommand.forEach((el) => {
Object.entries(el).forEach(([key, value]) => {
for (let ii = 1; ii <= value; ii++) {
newMission.resources?.push({
initialType: key,
status: 'unarranged',
});
}
});
});
};
I'm setting up a mongodb-memory-server in globalSetup.ts for Jest and copy the relevant data to the database from json-files.
globalSetup.ts
export = async function globalSetup() {
const instance = await MongoMemoryServer.create({
instance: { dbName: 'testDb' },
});
const uri = instance.getUri();
(global as any).__MONGOINSTANCE = instance;
process.env.MONGODB_URI = uri.slice(0, uri.lastIndexOf('/'));
process.env.JWT_SECRET = 'testSECRET';
const client = new MongoClient(
`${process.env.MONGODB_URI}/?maxPoolSize=20&w=majority`
);
try {
await client.connect();
const database = client.db('testDb');
database.createCollection('aao');
//#ts-ignore
await database.collection('aao').insertMany(aao['default']);
} catch (error) {
console.log(error);
} finally {
await client.close();
}
};
missionCreateHandler.test.ts
test('it adds the correct mission resources to the array', async () => {
const newMission = await Mission.create({
address: {
street: 'test',
houseNr: 23,
},
alarmKeyword: 'R1',
});
const expected = {
initialType: 'rtw',
status: 'unarranged',
};
addMissionResources('R1', newMission);
expect(newMission.resources[0].initialType).toEqual(expected.initialType);
expect(newMission.resources[0].status).toEqual(expected.status);
});
When runing the test, i get an 'TypeError: Cannot convert undefined or null to object at Function.values ()'. So it seems that the APP_DATA object is not set. I checked that the mongodb-memory-server is set up correctly and feed with the needed data.
When i hardcode the content of APP_DATA in index.ts, the test runs without problems.
So my questions are: How is the best practice to set up initial data in a node js app and where to store it (global object, simple variable and import it in the files where needed)? How can the test successfully run, or is my code just untestable?
Thank you!
I wanted to know if you could help me with the following error:
I'm trying to remove the EXPO SQLite base in React-Native and then import another base that I have in my folder.
When I do, it throws me the following error:
Error: no such table: users (code 1 SQLITE_ERROR): , while compiling:
this is my code:
import React, { useState, useEffect } from 'react';
const SQLite = require('expo-sqlite')
import * as FileSystem from 'expo-file-system';
import { Asset } from "expo-asset";
var db = null;
export default class SQLiteScreen extends React.Component {
constructor() {
super();
}
/*async openDatabase() {
if (!(await FileSystem.getInfoAsync(FileSystem.documentDirectory + 'SQLite')).exists) {
await FileSystem.makeDirectoryAsync(FileSystem.documentDirectory + 'SQLite');
}
await FileSystem.downloadAsync(
Asset.fromModule(require('./MainDB.db')).uri,
FileSystem.documentDirectory + 'SQLite/MainDB.db'
);
db = await SQLite.openDatabase(`MainDB.db`);
}
*/
async openDatabaseIShipWithApp() {
if (!(await FileSystem.getInfoAsync(`${FileSystem.documentDirectory}SQLite/MainDB.sqlite`)).exists) {
await FileSystem.makeDirectoryAsync(`${FileSystem.documentDirectory}SQLite/`, { intermediates: true });
const asset = await Asset.fromModule(require("./MainDB.db"));
await FileSystem.downloadAsync(asset.uri, `${FileSystem.documentDirectory}SQLite/MainDB.sqlite`);
}
db = await SQLite.openDatabase(`MainDB.sqlite`);
}
async deleteDatabaseWithApp(){
const internalDbName = "MainDB.sqlite"; // Call whatever you want
const sqlDir = FileSystem.documentDirectory + "SQLite/";
if ((await FileSystem.getInfoAsync(sqlDir + internalDbName)).exists){
await FileSystem.deleteAsync(sqlDir+internalDbName,
{idempotent:true})
}
}
ExecuteQuery = (sql, params = []) => new Promise(async (resolve, reject) => {
await this.openDatabaseIShipWithApp();
db.exec([{ sql: 'PRAGMA foreign_keys = ON ; ', args: [] }], false, () => { });
db.transaction((trans) => {
trans.executeSql(sql, params, (trans, results) => {
resolve(results);
},
(_, error) => {
reject(error);
});
});
});
}
I call the function as follows:
let query = `SELECT * FROM personal`;
await new SQLiteScreen().deleteDatabaseWithApp();
let selectQuery = await new SQLiteScreen().ExecuteQuery(query);
import axios from 'axios';
// const usersUrl = 'http://localhost:3003/users';
const usersUrl = 'http://localhost:8020/users';
export const getUsers = async (id) => {
id = id || '';
return await axios.get(`${usersUrl}/${id}`);
}
export const addUser = async (user) => {
return await axios.post(`${usersUrl}/add`, user);
}
export const deleteUser = async (id) => {
return await axios.delete(`${usersUrl}/${id}`);
}
export const editUser = async (id, user) => {
return await axios.put(`${usersUrl}/${id}`, user)
}
This my client code when I try to add user it adds the user and details in back end in mongo db but cant view it in the front end when I click on the specific user.
import React, { useState, useEffect } from "react";
import { Link, useParams } from "react-router-dom";
import axios from "axios";
const usersUrl = 'http://localhost:8020/users';
const View = () => {
const [user, setUser] = useState({
projectname: '',
projecttype: '',
numberofissuesreported: '',
retestlead: '',
progress: '',
startdate: '',
enddate: '',
});
const { id } = useParams();
useEffect(() => {
loadUser();
//getUsers();
}, []);
const loadUser = async () => {
const res = await axios.get(`http://localhost:8020/users/add`);
setUser(res.data);
};
And this is my view.jsx file.
As the error mentions clearly, if you use response.send, it sends the data to the client. You cannot modify the states once they are sent.
Corrected Code:
export const addUser = async (request, response) => {
// retreive the info of user from frontend
const user = request.body;
// Removed the line
const newUser = new User(user);
try{
await newUser.save();
response.status(201).json(newUser);
} catch (error){
return response.status(404).json({ message: error.message});
}
}
Tip: If you are debugging, just use console.log instead of response.send and see those values on the terminal.
Example:
export const addUser = async (request, response) => {
// retreive the info of user from frontend
const user = request.body;
console.log("Executing..");
console.log(user);
const newUser = new User(user);
try{
await newUser.save();
response.status(201).json(newUser);
} catch (error){
return response.status(404).json({ message: error.message});
}
}
I have a function that I want to return the array of objects parsedContacts. With the return statement there, the console.log above it prints an array of empty objects. When I remove the return statement, each object has three properties as expected.
How can I return the parsedContacts and include the properties?
/* eslint-disable no-console */
/* eslint-disable no-unused-vars */
import { PermissionsAndroid } from 'react-native';
import Contacts from 'react-native-contacts';
export const getAndProcessPhoneContacts = async () => {
PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.READ_CONTACTS,
{
'title': 'Contacts',
'message': 'Xxxxxxxx would like to view your contacts.'
}
)
const contacts = await getContacts();
const parsedContacts = parseContacts(contacts);
sortContacts(parsedContacts);
console.log(parsedContacts);
return parsedContacts; // this line needs to be removed for parsedContacts to have properties in the objects inside it.
}
const getContacts = () => {
return new Promise((resolve, reject) => {
Contacts.getAll((error, contacts) => {
contacts ? resolve(contacts) : reject(error)
})
})
}
const parseContacts = contacts => {
return contacts.map(contact => {
let parsedContact = {}
Object.keys(contact).forEach(key => {
switch (key) {
case 'givenName':
parsedContact.firstName = contact[key]
break
case 'familyName':
parsedContact.surname = contact[key]
break
case 'phoneNumbers':
parsedContact.phoneNumber = contact[key].length ? contact[key][0].number : ''
}
})
return parsedContact
})
}
const sortContacts = contacts => {
contacts.sort((a, b) => {
let contactA = a.firstName;
let contactB = b.firstName;
return (contactA < contactB) ? -1 : (contactA > contactB) ? 1 : 0;
});
}
Update
As requested in the comments below, here is the calling function of getAndProcessPhoneContacts. I know that this is ugly and needs refactoring, any advice on this gratefully accepted too!
async componentDidMount() {
ConnectyCube.init(...config)
try {
const accessToken = await getFirebaseToken();
if (accessToken) {
await authorizeConnectyCube(accessToken);
if (this.props.user.parsedContacts) {
const registeredUsers = await retrieveRegisteredUsers();
this.props.updateRegisteredContacts(registeredUsers);
Actions.Dashboard();
} else {
const parsedContacts = await getParsedContactsFromStorage();
if (parsedContacts) {
this.props.updateParsedContacts(parsedContacts);
Actions.Dashboard();
} else {
const parsedContacts = await getAndProcessPhoneContacts();
console.log(parsedContacts); // prints an array of empty objects
await writeParsedContactsToStorage(parsedContacts);
this.props.updateParsedContacts(parsedContacts);
const registeredUsers = await retrieveRegisteredUsers();
this.props.updateRegisteredContacts(registeredUsers);
Actions.Dashboard();
}
}
} else {
Actions.PhoneNumberInput();
}
} catch (error) {
Alert.alert(error);
}
}
Update 2
I have an inelegant solution by using a callback:
const cb = (ct) => {
console.log(ct); // Objects now have properties
}
const parsedContacts = await getAndProcessPhoneContacts(cb);
await writeParsedContactsToStorage(parsedContacts);
this.props.updateParsedContacts(parsedContacts);
const registeredUsers = await retrieveRegisteredUsers();
this.props.updateRegisteredContacts(registeredUsers);
Actions.Dashboard();
}
}
} else {
Actions.PhoneNumberInput();
}
} catch (error) {
Alert.alert(error);
}
}
And the called function:
export const getAndProcessPhoneContacts = async (cb) => {
PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.READ_CONTACTS,
{
'title': 'Contacts',
'message': 'Xxxxxxx would like to view your contacts.'
}
)
const contacts = await getContacts();
const parsedContacts = parseContacts(contacts);
sortContacts(parsedContacts);
console.log(parsedContacts);
cb(parsedContacts)
}
here I'm trying to have the sum of orders and the sum of their quantity in which I use Node JS for my backend. The problem is whenever I run my code -- my fetch functions seems not working properly or I'm missing something that I'm not aware.
But using postman, my API is working with the expected output. Buuut if I use it in my react-native code it show some errors.
Here's my code for backend:
OrderNo.js (models) //Backend
var Task = {
Sum:function(id,callback) {
return db.query("SELECT SUM(order_amount) AS TotalAmountOrdered FROM orders where order_no=?",[id],callback);
},
}
OrderNo.js (router) //Backend
var Task = require('../models/OrderNo');
router.get('/ForSum/:id?', (req, res, next) => {
Task.Sum(req.params.id,function(err,rows) {
if(err) {
res.json(err);
}
else {
res.json(rows);
}
});
});
NumOrder.js (models) //Backend
var Task = {
NumOrder:function(id,callback) {
return db.query("SELECT SUM(order_quantity) AS TotalItemsOrdered FROM orders where order_no=?",[id],callback);
},
}
NumOrder.js (router) //Backend
var Task = require('../models/NumOrder');
router.get('/num/:id?', (req, res, next) => {
Task.NumOrder(req.params.id,function(err,rows) {
if(err) {
res.json(err);
}
else {
res.json(rows);
}
});
});
And here's my code for React-Native
export default class Settlement extends Component {
constructor(props){
super(props)
this.state = {
orderDet: this.props.navigation.state.params.orderDet,
numOrder: [],
TotalSum: [],
};
}
fetchSum = async () => {
const response = await fetch("http://192.168.254.104:3308/OrderNo/ForSum/" + this.state.orderDet)
const json = await response.json()
this.setState({ TotalSum: json })
}
fetchNumOrders = async () => {
const response = await fetch("http://192.168.254.104:3308/NumOrder/num/" + this.state.orderDet )
const json = await response.json()
this.setState({ numOrder: json })
}
componentDidMount() {
this.fetchNumOrders();
this.fetchSum();
}
render() {
return (
<View>
<Text>Number of Orders: { this.state.numOrder }</Text>
<Text>Total Amount: ₱{ this.state.TotalSum }</Text>
</View>
)
}
}
And here is my DB
**PS: **I also tried " json[0].order_no " on each of my fetch function and there's no error, but my output is empty.
Based on your response object in the Postman, you need to do the following
this.state = {
orderDet: this.props.navigation.state.params.orderDet,
numOrder: null,
TotalSum: null,
};
fetchSum = async () => {
const response = await fetch("http://192.168.254.104:3308/OrderNo/ForSum/" + this.state.orderDet)
const json = await response.json()
this.setState({ TotalSum: json[0].TotalAmountOrdered })
}
fetchNumOrders = async () => {
const response = await fetch("http://192.168.254.104:3308/NumOrder/num/" + this.state.orderDet )
const json = await response.json()
this.setState({ numOrder: json[0].TotalItemsOrdered })
}
The error means that you cannot have object or array as the child the component i.e. <Text>. You can only have string or number displayed inside the component.
<Text>Number of Orders: { this.state.numOrder[0].TotalAmountOrdered }</Text>// Inside {} value of variable should be string or number not array or object
The error is that you are setting value of this.state.numOrder an array