Display SectionList from local json file - reactjs

I am trying to create a SectionList from a json file named notes.json. Basically one object in the notes json array will correspond to one SectionList entry. I already load the json array in notesData. However, when i try to use notesData as a source for my SectionList i get the error: TypeError: undefined is not an object (evaluating 'props.sections.reduce')
Here is my code:
import React from 'react';
import { Text, View, SectionList, ListItem, H1 } from 'react-native';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { styles } from '~/containers/Notes/styles';
import { notes } from './Notes.json';
const notesData = [];
Object.keys(notes).forEach((key) => {
notesData.push(notes[key]);
});
class NotesContainer extends React.Component {
render() {
return (
<View style={styles.container}>
<SectionList
renderItem={({ item }) => <ListItem title={item.RELEASE_NOTE} />}
renderSectionHeader={({ section }) => <Text title={section.RELEASE_VERSION} />}
sections={this.notesData}
/>
</View>
);
}
}
export { NotesContainer };
export default connect(null, null)(NotesContainer);
Here is my Notes.json
{
"notes": [
{
"RELEASE_VERSION": "0.1.1",
"RELEASE_DATE": "01 Mar 2018",
"RELEASE_NOTE": [
"General bug fixes"
]
},
{
"RELEASE_VERSION": "0.1.0",
"RELEASE_DATE": "01 Feb 2018",
"RELEASE_NOTE": [
"Initial Launch"
]
}
]
}

Your data structure for SectionList is not correct, it should have a data prop with the array of data you want to render in that section. Below there is an example for the data you have.
section
An object that identifies the data to be rendered for a given section.
Properties:
data array
The data for rendering items in this section. Array of
objects, much like FlatList's data prop.
Example
export default class App extends Component {
constructor() {
super();
this.notesData = Object.keys(notes).map((key) => {
return { data: notes[key].RELEASE_NOTE, version: notes[key].RELEASE_VERSION }
});
}
render() {
return (
<View style={styles.container}>
<SectionList
renderItem={({ item }) => <Text>{item}</Text>}
renderSectionHeader={({ section }) => <Text>{section.version}</Text>}
sections={this.notesData}
/>
</View>
);
}
}

You can do lists which based on your json data with ListView component;
constructor(props) {
super(props);
this.ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 });
this.listNotes = this.listNotes.bind(this);
this.state = {
notesData: this.ds.cloneWithRows({}),
};
}
componentWillMount() {
this.setState({ notesData: this.ds.cloneWithRows([notes]) });
}
renderState() {
return (
<View>
<ListView
dataSource={this.state.notesData}
enableEmptySections
renderRow={this.listNotes}
/>
</View>
);
}
listNotes(rowData) {
return (
<View>
{rowData.map((item, key) => (
<View key={key}>
...
</View>
))}
</View>
);
}
render() {
<ScrollView>
{this.renderState()}
</ScrollView>
}

Related

React native get information from json data

I'm a beginner in react native, so, what I wanted to do is to get an item.CPU in an array to be able to put it in sampleData so I can render the information in a graphic using PureChart.
import React, { Component } from 'react';
import { StyleSheet, ActivityIndicator, SafeAreaView, FlatList, Text, View } from
'react-native';
import PureChart from 'react-native-pure-chart';
export default class App extends Component {
constructor(props) {
super(props);
this.state = {data: []};
}
componentDidMount() {
fetch('http://192.168.1.34:3000/users').then((response) =>
response.json()).then((json)=> this.setState({data : json.param})).catch((error) =>
console.error(error));
}
render() {
const { data } = this.state;
let sampleData = [30, 200, 170, 250, 10];
return (
<View style = {styles.container}>
<PureChart data={sampleData} type='line' />
<SafeAreaView >
<FlatList data ={data} keyExtractor={({ID}, index) => ID} renderItem={({item})=>(<Text>{item.CPU}</Text>)}/>
</SafeAreaView>
</View>
// <SafeAreaView style={styles.container}>
// <FlatList
// data={data}
// keyExtractor={({ ID }, index) => ID}
// renderItem={({ item }) => (<Text>{item.CPU}, {item.RPM}</Text>)}
// />
//
);
}
};
const styles = StyleSheet.create({
container: {
top: "50%",
},
});
It might be easier to pass in the json data inside a prop. You should make this request before you load the view. That way you remove potential bugs that arise from things like server errors, errors from improperly handling loading etc.

React Native is Not fetching the latest data from API call

I sicerely Apologies if this has been asked before. I am a bit new to react native and react in general.
my react nativee code is not fetcing the latest data
see the code for the list component below
I would deeply appreciate it if you can tell me why this is happening and how to make it pick the latest data
import React, { Component } from "react";
import {
FlatList,
Text,
View,
StyleSheet,
ScrollView,
ActivityIndicator
} from "react-native";
import Constants from "expo-constants";
import { createStackNavigator } from "#react-navigation/stack";
import { toCommaAmount } from "./utilities";
const Stack = createStackNavigator();
function MyStack() {
return (
<Stack.Navigator>
<Stack.Screen name="Home" component={ExpenseList} />
<Stack.Screen name="NewTransaction" component={ExpenseForm} />
</Stack.Navigator>
);
}
function Item({ title }) {
return (
<View style={styles.item}>
<Text style={styles.title}>{title}</Text>
</View>
);
}
class ExpenseList extends Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
dataSource: null
};
}
componentDidMount() {
return fetch("https://example.com/expense/api/get_all.php")
.then(response => response.json())
.then(responseJson => {
this.setState({
isLoading: false,
dataSource: responseJson.items
});
})
.catch(error => console.log(error));
}
render() {
if (this.state.isLoading) {
return (
<View style={styles.container}>
<ActivityIndicator />
</View>
);
} else {
let myExpenses = this.state.dataSource.map((val, key) => {
return (
<View key={key} style={styles.item}>
<Text>
{val.title} {toCommaAmount(val.amount)}
</Text>
<Text>{val.date_time}</Text>
</View>
);
});
return <View style={styles.container}>{myExpenses}</View>;
}
}
}
export default ExpenseList;
ComponentDidMount is a void function. I assume you have this problem because you try to return the result of the fetch execution.
Can you remove your api call out of componentDidMount(), because it gets invoked only once. Rename it to getRealData() and attach it to a button click. So every click on button will being up latest data from backend.

React Component Props are receiving late. (Meteor JS)

I am working on a react-native and meteor js project.
My problem is that the props received from withTracker() function are only received in componentDidUpdate(prevProps) I don't get them in constructor or componentWillMount.
Another issue is when i pass props directly from parent to child. it receives them late due to which my component does not update
iconGroups prop comes from withTracker() method
and openSection props which i am using in this showGroupIcons()
is passed directly from parent to this component.
I want to open Accordian section that is passed to it via parent. but problem is in componentDidUpdate(prevProps) I am changing state due to which component re-renders.
openSection variable by default value is Zero. when props arrvies it value changes which i required But Accordian does not update.
Below is my code
import React, { Component } from 'react';
import Meteor, { withTracker } from 'react-native-meteor';
import {
View, Image, ScrollView, TouchableOpacity,
} from 'react-native';
import PopupDialog from 'react-native-popup-dialog';
import {Text, Icon, Input, Item, List,} from 'native-base';
import Accordion from 'react-native-collapsible/Accordion';
import { Col, Row, Grid } from 'react-native-easy-grid';
import styles from './styles';
import CONFIG from '../../config/constant';
import {MO} from "../../index";
const staticUrl = '../../assets/img/icons/';
class IconPickerComponent extends Component {
constructor(props) {
super(props);
this.state = {
dataSource: [],
itemName: 'apple1',
activeSections: 0,
showAccordian: true,
accordianData: []
};
}
componentDidUpdate(prevProps) {
if(prevProps.iconGroups !== this.props.iconGroups) {
let images = this.props.iconGroups.map(icon => icon.images);
let flatten = [].concat.apply([], images).map(img => { return {name: img, icon: CONFIG.ICON_URL+img+'.png'} })
this.setState({ filteredItems: flatten, dataSource: flatten, accordianData: this.props.iconGroups });
}
}
componentDidMount() {
this.props.onRef(this);
}
componentWillUnmount() {
this.props.onRef(null);
}
method() {
// this.setState(...this.state,{
// searchText: ''
// })
this.iconPicker.show(); // show icon picker
}
onSearchChange(text) {
this.setState({
showAccordian: !(text.length > 0)
});
const searchText = text.toLowerCase();
const filteredItems = this.state.dataSource.filter((item) => {
const itemText = item.name.toLowerCase();
return itemText.indexOf(searchText) !== -1;
});
this.setState({ filteredItems });
}
onIconSelect(item) {
this.setState({
itemName: item,
});
this.iconPicker.dismiss();
if (this.props.onIconChanged) {
this.props.onIconChanged(item);
}
}
_renderSectionTitle = section => {
return (
<View style={styles.content}>
<Text></Text>
</View>
);
};
_renderHeader = section => {
return (
<View style={styles.accordHeader}>
<Text style={{color: 'white'}}>{this.state.showAccordian} - {section.group}</Text>
<Text>
<Icon style={styles.downArrow} name="ios-arrow-down" />
</Text>
</View>
);
};
_renderContent = section => {
return (
<View style={styles.accordContent}>
{
section.images.map((img, key) => (
<TouchableOpacity onPress={() => this.onIconSelect(img)} key={key}>
<View style={styles.iconsGrid}>
<Image style={styles.image} source={{uri: CONFIG.ICON_URL+ img + '.png'}}/>
</View>
</TouchableOpacity>
))
}
</View>
);
};
_updateSections = activeSections => {
this.setState({ activeSections });
};
hasGroupIcons() {
return this.props.iconGroups.length > 0;
};
showGroupIcons() {
if(this.state.showAccordian){
let openSection;
if(!!this.props.openSection) {
let groupIndex = this.state.accordianData.findIndex(icon => icon.group === this.props.openSection);
if(groupIndex !== -1) {
openSection = groupIndex;
} else {
openSection = 0;
}
} else {
openSection = 0;
}
return(<Accordion
sections={this.state.accordianData}
activeSections={this.state.activeSections}
renderSectionTitle={this._renderSectionTitle}
renderHeader={this._renderHeader}
renderContent={this._renderContent}
onChange={this._updateSections}
initiallyActiveSection={openSection} />);
} else {
return(<View style={{flexWrap: 'wrap', flexDirection: 'row'}}>
{
this.state.filteredItems.map((item, key) => (
<TouchableOpacity onPress={() => this.onIconSelect(item.name)} key={key}>
<View style={styles.iconsGrid}>
<Image style={styles.image} source={{uri: item.icon}}/>
</View>
</TouchableOpacity>
))
}
</View>)
}
};
render() {
return (
<PopupDialog
overlayOpacity={0.8}
overlayBackgroundColor="#414141"
dialogStyle={styles.dialogBox}
containerStyle={styles.dialogContainer}
ref={(popupDialog) => { this.iconPicker = popupDialog; }}
>
<ScrollView>
<View style={styles.dialogInner}>
<Item searchBar rounded style={styles.searchbar}>
<Icon style={styles.searchIcon} name="search" />
<Input onChangeText={this.onSearchChange.bind(this)} style={styles.inputSearch} placeholder="Search" />
</Item>
{
this.hasGroupIcons() && this.showGroupIcons()
}
</View>
</ScrollView>
</PopupDialog>
);
}
}
export default withTracker(params => {
MO.subscribe('ipSubsId3', 'IconGroups');
return {
iconGroups: MO.collection('IconGroups', 'ipSubsId3').find({}),
};
})(IconPickerComponent);
I am new to react. I am assuming when props change component re-renders.
Use this life cycle method
static getDerivedStateFromProps(prevProps, prevState) {
if(prevProps.iconGroups !== this.props.iconGroups) {
let images = this.props.iconGroups.map(icon => icon.images);
let flatten = [].concat.apply([], images).map(img => { return {name: img, icon: CONFIG.ICON_URL+img+'.png'} })
this.setState({ filteredItems: flatten, dataSource: flatten, accordianData: this.props.iconGroups });
}
}
getDerivedStateFromProps is invoked right before calling the render method, both on the initial mount and on subsequent updates. It should return an object to update the state, or null to update nothing.
Read more about this lifecycle method here
I have fixed this issue. Actually my concepts were not right. I thought props are first received in constructor and componentWillMount. But I get all props in render() and everything works fine i dont have to use any lifecycle method to use props now

Access passed data with StackNavigation outside render

I'm able to fetch a ListView and show details page onPress of any of the list item. I can show the details of the clicked item in the DetailsPage but only in the render(). How to access any of the value outside of the render? I want to use that value to fetch information from another API
Main Page:
import React, { Component } from 'react';
import {
AppRegistry, StyleSheet, ListView,
Text, TouchableHighlight, View
} from 'react-native';
import { StackNavigator } from 'react-navigation';
import DetailsPage from './src/DetailsPage';
class HomeScreen extends React.Component {
static navigationOptions = {
title: 'MyApp!',
};
constructor() {
super();
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
userDataSource: ds,
};
}
componentDidMount(){
this.fetchUsers();
}
fetchUsers(){
fetch('https://jsonplaceholder.typicode.com/users')
.then((response) => response.json())
.then((response) => {
this.setState({
userDataSource: this.state.userDataSource.cloneWithRows(response)
});
});
}
renderRow(user, sectionID, rowID, highlightRow){
const { navigate } = this.props.navigation;
return(
<TouchableHighlight onPress={() => navigate('DetailsPage', {users:user })}>
<View style={styles.row}>
<Text style={styles.rowText}> {user.name} </Text>
</View>
</TouchableHighlight>
)
}
render(){
return(
<ListView
dataSource = {this.state.userDataSource}
renderRow = {this.renderRow.bind(this)}
/>
)
}
}
const NavigationTest = StackNavigator({
Home: { screen: HomeScreen },
DetailsPage: { screen:DetailsPage },
});
AppRegistry.registerComponent('NavigationTest', () => NavigationTest);
Details Page:
import React, { Component } from 'react';
import { StyleSheet, ListView, Text, TouchableHighlight, View } from 'react-native';
export default class DetailsPage extends React.Component {
static navigationOptions = ({ navigation }) => ({
title: `${navigation.state.params.users.name}`,
});
// var userid = 2; --> This doesn't work as it returns Unexpected Token where var is mentioned.
constructor() {
super();
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
albumDataSource: ds,
};
}
componentDidMount(){
this.fetchAlbums();
}
fetchAlbums(){
var theUser = 2; //This is the value I want to receive from the clicked user.
// var newUser = this.props.navigation.state.users.id; -> this doesnt work.
fetch('https://jsonplaceholder.typicode.com/albums?userId='+newUser)
.then((response) => response.json())
.then((response) => {
this.setState({
albumDataSource: this.state.albumDataSource.cloneWithRows(response)
});
});
}
renderRow(album, sectionID, rowID, highlightRow){
return(
<TouchableHighlight>
<View style={styles.row}>
<Text style={styles.rowText}> {album.userId} - {album.title} </Text>
</View>
</TouchableHighlight>
)
}
render() {
const { params } = this.props.navigation.state;
return (
<View style={styles.container}>
<Text style={styles.textstyle}>Screen Chat with {params.users.name}</Text>
<Text style={styles.textstyle}>Username : {params.users.username}</Text>
<Text style={styles.textstyle}>Email : {params.users.email}</Text>
<Text style={styles.textstyle}>ID : {params.users.id}</Text>
<ListView
dataSource = {this.state.albumDataSource}
renderRow = {this.renderRow.bind(this)}
/>
</View>
);
}
}
So I want to use the users.id to fetch more data on the DetailsPage and display that data. How do I do that. Please help. Many thanks.
Here what i have found , this look wrong to me,
var newUser = this.props.navigation.state.users.id;
Use this may be it can help you
console.log(this.props.naviagtion.state.params) **Check if are getting those value then use below one **
var newUser = this.props.navigation.state.params.users.id;

How to access data in firebase from react-native?

I am having trouble rendering data from firebase.
Here is my current data structure on firebase:
{name: "sleep"}
ie. that's the only object the database contains atm.
Using a ListView, it renders a row (shows the background and border of the row according to the number of items in the database), however, there is no text inside the row..
ListView code:
<ListView
enableEmptySections={true}
dataSource={this.state.dataSource}
renderRow={this._renderItem.bind(this)}
/>
_renderItem(task) {
return (
<ListItem task={task} />
);
}
Code for ListItem:
class ListItem extends Component {
render() {
return (
<View style={styles.listItem}>
<Text style={styles.listItemTitle}>{this.props.task.name}</Text>
</View>
);
}
}
Code used to listen to tasks
listenForTasks(tasksRef) {
tasksRef.on('value', (dataSnapshot) => {
var tasks = [];
dataSnapshot.forEach((child) => {
tasks.push({
name: child.val().name,
_key: child.key
});
console.log(dataSnapshot.val()); // returns JSON object of {name: 'sleep'}
console.log(tasks); // returns an array containing the object of {name: 'sleep'}
console.log(dataSnapshot.val().name); // returns 'sleep'
});
this.setState({
dataSource: this.state.dataSource.cloneWithRows(tasks)
});
});
}
You need to setup a constructor for your ListItem Component:
class ListItem extends Component {
//Add this....
constructor(props){
super(props)
}
//--------------
render() {
return ( <View style={styles.listItem}>
<Text style={styles.listItemTitle>
{this.props.task.name}
</Text>
</View> );
}
}

Resources