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('')
.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);
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/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);
Share
Improve this question
edited Jul 21, 2017 at 14:58
nem035
35.5k6 gold badges92 silver badges104 bronze badges
asked Jul 21, 2017 at 14:39
Daniel CabreraDaniel Cabrera
431 gold badge1 silver badge7 bronze badges
1
- don't return to state null value use empty str "" – Omar bakhsh Commented Jan 26, 2021 at 3:34
2 Answers
Reset to default 2From 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)
})
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 ponent (along with its state) is created before that call resolves.
constructor() {
fetch('http://xxxxx.mybluemix/get')
// ...
.then(() => {
// ...
// this code gets called after the ponent 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/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>
);
}