Reputation: 43
I am running the following code in Android emulator but I am getting null is not an object (evaluating 'this.state.dataSource') error.
Please, could you help me to see what I am doing wrong? For some reason the line dataSource={this.state.dataSource}
is getting null.
import React, {
Component
} from 'react';
import {
AppRegistry,
ActivityIndicator,
ListView,
Text,
View,
StyleSheet
} from 'react-native';
import Row from './Row';
import Header from './Header';
import SectionHeader from './SectionHeader';
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 20,
},
separator: {
flex: 1,
height: StyleSheet.hairlineWidth,
backgroundColor: '#8E8E8E',
},
});
export default class NoTocarList extends Component {
constructor(props) {
super(props);
const getSectionData = (dataBlob, sectionId) => dataBlob[sectionId];
const getRowData = (dataBlob, sectionId, rowId) =>
dataBlob[`${rowId}`];
fetch('http://xxxxx.mybluemix.net/get')
.then((response) => response.json())
.then((responseJson) => {
const ds = new ListView.DataSource({
rowHasChanged: (r1, r2) => r1 !== r2,
sectionHeaderHasChanged: (s1, s2) => s1 !== s2,
getSectionData,
getRowData
});
const {
dataBlob,
sectionIds,
rowIds
} =
this.formatData(responseJson);
this.state = {
dataSource: ds.cloneWithRowsAndSections(dataBlob, sectionIds,
rowIds)
}
})
}
formatData(data) {
const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
const dataBlob = {};
const sectionIds = [];
const rowIds = [];
for (let sectionId = 0; sectionId < alphabet.length; sectionId++) {
const currentChar = alphabet[sectionId];
const users = data.filter((user) =>
user.calle.toUpperCase().indexOf(currentChar) === 0);
if (users.length > 0) {
sectionIds.push(sectionId);
dataBlob[sectionId] = {
character: currentChar
};
rowIds.push([]);
for (let i = 0; i < users.length; i++) {
const rowId = `${sectionId}:${i}`;
rowIds[rowIds.length - 1].push(rowId);
dataBlob[rowId] = users[i];
}
}
}
return {
dataBlob,
sectionIds,
rowIds
};
}
render() {
return (
<View style={{flex: 1, paddingTop: 20}}>
<ListView
style={styles.container}
dataSource={this.state.dataSource}
renderRow={(rowData) => <Row {...rowData} />}
renderSeparator={(sectionId, rowId) => <View key={rowId} />}
style={styles.separator}
renderHeader={() => <Header />}
renderSectionHeader={(sectionData) => <SectionHeader {...sectionData} />}
/>
</View>
);
}
}
AppRegistry.registerComponent('NoTocar', () => NoTocarList);
Upvotes: 2
Views: 10984
Reputation: 35491
The issue is that you're trying to update the state asynchronously, after a render, but are expecting the result on the first render. Another issue is that you're overwriting the state instead of updating it.
The fetch
call in your constructor is async, meaning the constructor is finished and the component (along with its state) is created before that call resolves.
constructor() {
fetch('http://xxxxx.mybluemix.net/get')
// ...
.then(() => {
// ...
// this code gets called after the component is created
// this state is also overwriting already created state
this.state = {
dataSource: ds.cloneWithRowsAndSections(dataBlob, sectionIds,
rowIds)
}
})
}
Since your data is obtained asynchronously, you can add a check and show a progress indicator while its loading (you should also use setState
instead of overwriting the state):
constructor() {
this.state = {
dataSource: null // initialize it explicitly
}
fetch('http://xxxxx.mybluemix.net/get')
// ...
.then(() => {
// ...
// use set state
this.setState({
dataSource: ds.cloneWithRowsAndSections(dataBlob, sectionIds,
rowIds)
})
})
}
render(){
// on initial render, while the state hasn't been fetched yet
// show the spinner
if (!this.state.dataSource) {
return <ActivityIndicator />
}
return(
<View style={{flex: 1, paddingTop: 20}}>
...
</View>
);
}
Upvotes: 2
Reputation: 4650
From your example you have passed the dataSource
as null
to the ListView
. So you need to initialize it first by using
this.state({
dataSource: {}
})
After getting the response from the Api
call you need to set the dataSource state
by using
this.setState({
dataSource: ds.cloneWithRowsAndSections(dataBlob, sectionIds,
rowIds)
})
Upvotes: 2