I have this block of code that handles the component's state:
componentWillMount(){
this.state = {
datasource: new ListView.DataSource({rowHasChanged: (row1, row2) => row1 !== row2})
}
db.transaction((tx) => {
tx.executeSql("SELECT * FROM schedules", [], (tx, res) => {
let len = res.rows.length;
if(len > 0) {
for(let i = 0; i < len; i++) {
obj.push({id: res.rows.item(i)["id"], title: res.rows.item(i)["title"]})
}
this.setState({
datasource: this.state.datasource.cloneWithRows(obj)
})
var data = this.state.datasource;
console.log(data);
} else {
console.log("empty")
}
})
}, (err) => {
console.log("error: " + JSON.stringify(err))
})
}
It works like a charm when the app launches the first time. But whenever I re-enter the page again the values inside datasource get duplicated. Shouldn't rowHasChanged: (row1, row2) => row1 !== row2 force the ListView not to behave like that or am I missing a logic that needs to be added to the code?
ListView bug example:
========================
| First title, id: 1 |
========================
| Second title, id: 2 |
========================
| First title, id: 1 |
========================
| Second title, id: 2 |
When I console logged the datasource I saw this property: _cachedRowCount:15
Is there a way to force react native not to cache this.state.datasource and re-run every time the page gets open?
I ended up solving that issue by emptying the obj array like so:
db.transaction((tx) => {
tx.executeSql("SELECT * FROM schedules", [], (tx, res) => {
let len = res.rows.length;
if(len > 0) {
for(let i = 0; i < len; i++) {
obj.push({id: res.rows.item(i)["id"], title: res.rows.item(i)["title"]}) // Array that needs to be emptied
}
this.setState({
datasource: this.state.datasource.cloneWithRows(obj)
})
obj = [] // Done here
}
})
}, (err) => {
console.log("error: " + JSON.stringify(err))
})
Related
Hello i'm new with axios and i wanna to display all my data from subpages to screen but I have some trouble because when I display them it shows me the data from the last subpage instead of all of them at the bottom I throw the code how I download the data through axios. How do I display them all ?
const [data, setData] = useState([]);
async function test() {
for (let id = 1; id < 3; id ++) {
axios.get(`https://api.jsonbin.io/b/61f98c361960493ad1865911/${id}`)
.then(({data}) => {
setData(data.commentData)
console.log(data.commentData)
})
.catch((error) => console.error(error))
}
}
useEffect(() => {
test()
}, []);
You are overwriting your data state in each loop, so the last loop iteration is the one that you see.
async function test() {
for (let id = 1; id < 3; id++) {
axios.get(`https://api.jsonbin.io/b/61f98c361960493ad1865911/${id}`)
.then(({ data }) => {
setData(data.commentData) // <-- overwrites previous state
console.log(data.commentData)
})
.catch((error) => console.error(error));
}
}
Use a functional state update to correctly update from the previous state.
async function test() {
setData([]);
for (let id = 1; id < 3; id++) {
axios.get(`https://api.jsonbin.io/b/61f98c361960493ad1865911/${id}`)
.then(({ data }) => {
setData(data => [...data, data.commentData]) // <-- append new data
console.log(data.commentData)
})
.catch((error) => console.error(error));
}
}
You could also map an array of Promises and use Promise.all to get a single array of resolved data values.
async function test() {
const requests = [1,2,3].map(id => axios.get(`https://api.jsonbin.io/b/61f98c361960493ad1865911/${id}`));
try {
const dataArr = await Promise.all(requests);
setData(dataArr.map(({ data }) => data.commentData));
} catch(error) {
console.error(error);
}
}
I'm still learning to use React Native and runnig into an issue with the stack size being exceeded but I'm unsure why. Looking at other posts I see it must be that the screen is being rerendered too many times and is stuck in a loop but how can I prevent this happening?
RaceListScreen
export function RandomRaceScreen(this: any, {navigation: navigation}) {
const [raceList, setRaceList] = useState<RaceModel[]>([]);
useEffect(() => {
const fetchedRaces: RaceModel[] = getCoreRaceList();
setRaceList(fetchedRaces);
}, []);
//number of players must be less than max number of available races
const racePressed = (raceId: number) => {
console.log('Displaying info about Race, ', raceId);
navigation.navigate('RaceLoreListScreen', {raceId: raceId});
};
const renderRaces = (item: unknown) => {
return (
<RaceCard
race={item.item}
onClick={() => {
racePressed(item.item._groupId);
}}
/>
);
};
const width = Dimensions.get('window').width;
return (
<ImageBackground
source={require('../../assets/space_background_reduced_v1.png')}
style={globalStyles.background}>
<FlatList
data={raceList}
renderItem={renderRaces}
sliderWidth={width}
containerCustomStyle={style.carousel}
contentContainerCustomStyle={style.card}
itemWidth={width * 0.8}
layout="default"
removeClippedSubviews={false}
/>
</ImageBackground>
);
}
getCoreRaceList function:
import {RaceModel} from '../../models/RaceModel';
import races from '../../races/core_races.json';
export function getCoreRaceList(): RaceModel[] {
let raceList: RaceModel[] = [];
for (let i = 0; i < 5; i++) {
raceList.push(
new RaceModel(races[i], races[i].name, races[i].homeworld, false),
);
}
return raceList;
}
I am using Ionic Framework. I want to ask for help about how to insert more than 1000 rows at a time and while insertion showing a loading spinner to user so that there wont be any mess in database.
First, I have two services/factories.
Database :
.factory('DB', function ($ionicPopup, $cordovaSQLite, $q, $ionicPlatform, $cordovaNetwork,$ionicLoading) {
var self = this;
self.query = function (query, parameters) {
parameters = parameters || [];
var q = $q.defer();
$ionicPlatform.ready(function () {
$cordovaSQLite.execute(db, query, parameters)
.then(function (result) {
console.log(result);
q.resolve(result);
}, function (error) {
console.log(error+" .."+error.message);
alert('I found an error' + error.message);
q.reject(error);
});
});
return q.promise;
}
// Proces a result set
self.getAll = function (result) {
var output = [];
for (var i = 0; i < result.rows.length; i++) {
output.push(result.rows.item(i));
}
return output;
}
// Proces a single result
self.getById = function (result) {
var output = null;
output = angular.copy(result.rows.item(0));
return output;
}
return self;
})
Secondly, AsyncService for downloading data from multiple urls
.service('asyncService', function ($http, $q) {
return {
loadDataFromUrls: function (urls) {
alert("I am inside new Service ");
var deferred = $q.defer();
var urlCalls = [];
angular.forEach(urls, function (url) {
urlCalls.push($http.get(url.url));
});
// they may, in fact, all be done, but this
// executes the callbacks in then, once they are
// completely finished.
$q.all(urlCalls)
.then(
function (results) {
deferred.resolve(results)
},
function (errors) {
console.log(errors);
deferred.reject(errors);
},
function (updates) {
console.log(updates);
deferred.update(updates);
});
return deferred.promise;
}
};
})
And the method that firstly, should download the datas and then insert them into their belonged tables.
asyncService.loadDataFromUrls(urLs).then(function (result) {
DB.query("DELETE FROM INV");
// when i have to update the tables, i first delete them and then fill them back again.
$ionicLoading.show({
template : 'Processing into Database. Please Wait...',
timeout : '6000'
});
result.forEach(function (rows) {
console.log(rows.config.url);
var i = 0;
if (rows.config.url == 'http://11.444.222.55:55/mobil_op?op=get_inv') {
rows.data.forEach(function (entry) {
var parameters = [entry.code, entry.name,];
DB.query("INSERT INTO INV (CODE,NAME,......) VALUES(?,?........)",parameters);
})
}
})
}, function (err) {
alert("OOpsss ! : " + err.message);
console.log("error");
}, function (updates) {
alert("updates");
console.log("updates" + updates);
})
How should I work while inserting 4453 elements into array ?
db.executeSql("BEGIN TRANSACTION",{})
.then(( res )=> {
console.log(res);
// DO ALL INSERTION ON THE TABLES
// e.g
// AT LAST
db.executeSql("COMMIT",{})
.then(()=> console.log("COMMIT")
.catch(e => console.log(e));
}).catch(e => console.log(e));
// This process provided me much more faster way . The method above in the question dialog was inserting 4553 items in 3 minutes on Asus Zenphone 1. This method let me insert 10000 items less then 3 minutes.
I am trying to fetch from db-models to ListView. Here is my code:
export default class todoDB extends Component {
constructor(props) {
super(props);
this.state = {
dataSource : new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2})
};
}
componentDidMount () {
this.fetchData();
}
fetchData () {
DB.users.get_all(function(result) {
let data = [];
for(let i = 1; i <= result.totalrows; i++) {
data.push(result.rows[i]);
}
this.setState({
dataSource: dataSource.cloneWithRows(data),
})
});
}
render () {
return (
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderList}
/>
);
}
renderList (item) {
return (
<View>
<Text>{item.age}</Text>
</View>
);
}};
After running I don`t have error or any output, only empty screen.
I using
"react": "15.4.2",
"react-native": "0.40.0"
"react-native-db-models": "^0.1.3"
I have not tested it but you could try this :
fetchData() {
DB.users.get_all((result) => {
let data = [];
for (let i = 1; i <= result.totalrows; i++) {
data.push(result.rows[i]);
}
this.setState((prevState) => (
{
dataSource: prevState.dataSource.cloneWithRows(data)
}));
});
}
Changes are : using arrow function instead of function(result) to keep the scope of this and updating the previous state prevState.dataSource.cloneWithRows(data) (in your code dataSource was undefined here)
As to why use prevState, it is to keep immutability. Little bit more info here : https://facebook.github.io/react/docs/react-component.html#setstate
I am using SQLite as the device's database. What I am trying to basically achieve is this:
1- Give a user the ability to star his favorite "data"
2- Once the data gets saved in the db, retrieve it inside another page and insert them into a listView for the user to see at any time.
But no matter how much I try, I am always getting the same error.
Cannot read property of undefined.
The code:
import React, { Component } from 'react'
import {
View,
Text,
ListView
} from 'react-native'
var SQLite = require('react-native-sqlite-storage')
var db = SQLite.openDatabase({ name: "RHPC.db", location: "default"})
var obj;
class Schedules extends Component {
constructor(props) {
super(props)
const ds = new ListView.DataSource({
rowHasChanged: (r1, r2) => r1 !== r2
});
this.state = {
datasource: []
}
db.transaction((tx) => {
tx.executeSql("SELECT * FROM schedules", [], (tx, res) => {
let len = res.rows.length;
if(len > 0) {
for(let i = 0; i < len; i++) {
var obj = [{id: res.rows.item(i)["id"], title: res.rows.item(i)["title"]}]
}
this.setState({
datasource: obj
})
} else {
console.log("empty")
}
})
}, (err) => {
console.log("error: " + JSON.stringify(err))
})
}
_renderRow(rowData) {
return(
<View>
<Text key={rowData.id}>
{rowData.title}
</Text>
</View>
)
}
render() {
console.log(this.state.datasource);
return(
<View style={{marginTop: 150}}>
<ListView
dataSource={this.state.datasource}
renderRow={this._renderRow.bind(this)}
/>
</View>
);
}
}
const styles = {
}
export default Schedules;
When I try to console.log the dataSource state:
0: Object
id: 2
title: "Session 1: Transition from Humanitarian Assistance to Rebuilding Health & Health Systems."
So in other words it looks like it's working but not 100%? Because I do have two rows inside that table and it's only retrieving the last one. Is this the cause of the undefined issue?
You use ListView in a wrong way, you create new dataSource in constructor (ds) and not assign it anywhere, checkout example in documentation: https://facebook.github.io/react-native/docs/listview.html
It should be:
constructor(props) {
super(props)
this.state = {
dataSource: new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2}),
}
}
And in setState make something like this:
this.setState({
datasource: this.state.dataSource.cloneWithRows(obj)
})
Edit:
And in your for loop you should have:
var obj = [];
for(let i = 0; i < len; i++) {
obj.push({id: res.rows.item(i)["id"], title: res.rows.item(i)["title"]});
}