Cards of Native Base dynamically in react native and Firebase - react-native

I have data to extract from Firebase and i did. It's displayed perfectly fine in a listview. But now i have to display them in cards from the native base . this is the code i tried https://github.com/GeekyAnts/NativeBase/issues/347 but i get an error : undefined is not an object
import CardItem from'./CardItem'
import {Container, Content, Card, Body, Title} from 'native-base';
export default class SixteenTab extends Component {
constructor(props) {
super(props);
this.itemsRef = this.getRef().child('docs/topics/topics_2016/');
}
componentDidMount() {
// start listening for firebase updates
this.listenForItems(this.itemsRef);
}
getRef() {
return firebaseApp.database().ref();
}
listenForItems(itemsRef) {
itemsRef.on('value', (snap) => {
var items = [];
snap.forEach((child) => {
items.push({
title: child.val().title,
_key: child.key
});
});
this.setState({
items: items
});
});
}
render() {
return (
<View>
<Card dataArray={this.state.items}
renderRow={(item) => this._renderItem(item)}>
</Card>
</View>
)
}
_renderItem(item) {
return (
<CardItem item={item}/>
);
}
}
CardItem Page
class CardItem extends Component {
render() {
return (
<View style={styles.listItem}>
<Text style={styles.liText}>{this.props.item.title}</Text>
</View>
);
}
}
That's the code i used but i keep getting an error like the image below --> any idea please
PS: all the items are been extracted from firebase database correctly, since i can see them in the console
After putting this line this.state = { items: [] }; in the constructor, i get this warning
when trying the second method of Irfan , i get this warning and nothing is displayed in the screen
that's the final i wrote and still not working
export default class SixteenTab extends Component {
constructor(props) {
super(props);
this.itemsRef = this.getRef().child('docs/topics/topics_2016/');
this.state = { items: null };
}
componentDidMount() {
this.listenForItems(this.itemsRef);
}
componentWillMount() {
this.setState({
items:[]
});
}
getRef() {
return firebaseApp.database().ref();
}
listenForItems(itemsRef) {
itemsRef.on('value', (snap) => {
var items = [];
snap.forEach((child) => {
items.push({
title: child.val().title,
_key: child.key
});
});
this.setState({
items: items
});
});
}
render() {
return (
<View>
<Content>
<Card>
{
this.state.items.map((item,index)=>{
return (
<CardItem key={index}>
<View>
<Text>{item.title}</Text>
</View>
</CardItem>
)
})
}
</Card>
</Content>
</View>
)
}
_renderItem(item) {
return (
<ListItem item={item}/>
);
}

As per native base doc, dataArray and renderRow is not support Card Component. So should update your render function.
render() {
return (
<Container>
<Content>
<Card>
{
this.state.items.map((item, index)=>{
return (
<CardItem key={index}>
<View>
<Text>{item.title}</Text>
</View>
</CardItem>
)
})
}
</Card>
</Content>
</Container>
)
}

If you want your cards SEPARATELY , you need to put the key attribute in Card, not in CardItem! Like this:
render() {
return (
<Container>
<Content>
{
this.state.items.map((item, index)=>{
return (
<Card key={index}>
<CardItem>
<Text>{item.title}</Text>
</CardItem>
<Card/>
)
})
}
</Content>
</Container>
)
}

Related

How do you use checkbox in map function in React Native?

My current code is like this.
export default class All extends React.Component {
constructor(props) {
super(props);
this.state = {
items: [],
checkedItems: new Map(),
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(id) {
this.setState((prevState) => ({ checkedItems: prevState.checkedItems.set(id, true) }));
console.log(this.state.checkedItems);
}
async componentDidMount() {
const items = ...;
this.setState({ items });
}
render() {
const { items } = this.state;
return (
<Container>
<Content>
<View>
{items.map((item) => (
<View key={item.id}>
<Text>{item.name}</Text>
<View>
<CheckBox
checked={this.state.checkedItems.get(item.id)}
onPress={() => this.handleChange(item.id)}
/>
</View>
</View>
))}
</View>
</Content>
</Container>
);
}
}
I would like to know how to uncheck.
Also, when I firstly check, console.log() in handleChange() outputs Map {}.
Is it correct?
Or if you know better way bay to use checkbox in map function, plz tell me.
I would appreciate it if you could give me advices.

React Native reusable edit component

I'm trying to create a reusable component in react native. The idea is to have only one component responsible to edit all the fields that I have.
Main Component
...
constructor(props) {
super(props);
this.state.FirstName = 'Joe'
}
...
const { FirstName } = this.state.FirstName;
<TouchableOpacity
onPress={() =>
NavigationService.navigate('EditData', {
label: 'First Name',
initialValue: FirstName,
onSubmit: (FirstName) => this.setState({ FirstName })
})
}
>
<CardItem>
<Left>
<FontAwesome5 name="user-edit" />
<Text>First Name</Text>
</Left>
<Right>
<Row>
<Text style={styles.valueText}>{FirstName} </Text>
<Icon name="arrow-forward" />
</Row>
</Right>
</CardItem>
</TouchableOpacity>
// Keep doing the same for other fields
Then, the edit component should be reusable.
constructor(props) {
super(props);
// callback function
this.onSubmit = props.navigation.getParam('onSubmit');
// label/value
this.state = {
label: props.navigation.getParam('label'),
value: props.navigation.getParam('initialValue')
};
}
render() {
const { onSubmit } = this;
const { label, value } = this.state;
return (
<Container>
<Header />
<Content>
<Item floatingLabel style={{ marginTop: 10 }}>
<Label>{label}</Label>
<Input
value={value}
onChangeText={val => this.setState({ value: val })}
/>
</Item>
<Button
onPress={() => {
onSubmit(value);
NavigationService.navigate('TenantDetails');
}
}
>
<Text>OK</Text>
</Button>
</Content>
</Container>
);
}
When back to the main component, the first name value was not changed.
My NavigationService in case it might be the problem:
import { NavigationActions } from 'react-navigation';
let _navigator;
function setTopLevelNavigator(navigatorRef) {
_navigator = navigatorRef;
}
function navigate(routeName, params) {
_navigator.dispatch(
NavigationActions.navigate({
routeName,
params,
})
);
}
// add other navigation functions that you need and export them
export default {
navigate,
setTopLevelNavigator,
};
Thanks
You could pass a callback to your new component which handles this. The new component would start with a state with the initialValue set. It looks like you might be using react-navigation so I would recommend that if you want this component on its own screen you could do
this.navigation.navigate('SetValueScreen', {
initialValue: this.state.email,
onSubmit: (email) => this.setState({ email })
})
and on the SetValueScreen get the initialValue in the constructor and in the render use the callback
class SetValueScreen extends React.PureComponent{
constructor(props){
super(props)
this.onSubmit = props.navigation.getParam('onSubmit');
this.state = {
value: props.navigation.getParam('initialValue')
}
}
render(){
const { onSubmit } = this
const { value } = this.state
return (
...
<Right>
<TextInput value={value} onChangeText={(value) => setState({ value })} />
</Right>
<Button onPress={() => {
onSubmit(value)
navigation.goBack()
}} >
OK
</Button>
...
)
}
}
I hope this helps.

View inside curly braces not showing

I'm new in ReactNative. I'm following a tutorial from Udemy. I've been trying to show a value from a variable. its working in the instructor's video but not in my code. the code is given below:
export default class App extends React.Component {
state = {
placeName: '',
places: []
}
placeNameChangedHandler = val => {
this.setState({
placeName: val
})
}
placeSubmitHandler = () => {
if (this.state.placeName.trim() === "") {
return;
}
this.setState(prevState => {
return {
places: prevState.places.concat(prevState.placeName)
}
})
}
render() {
const placesOutput = this.state.places.map(place => {
<Text>{place}</Text>
})
return (
<View style={styles.container}>
<View style={styles.inputContainer}>
<TextInput
placeholder="Type something"
style={styles.placeInput}
value={this.state.placeName}
onChangeText={this.placeNameChangedHandler}/>
<Button
style={styles.placeButton}
onPress={this.placeSubmitHandler}
title="Add"/>
</View>
<View>
{this.placesOutput}
</View>
</View>
);
}
}
but the {placeOutput} is not showing anything. its working in the instructor's video but not in my code. What am I doing wrong?
You aren't returning anything in your map() function. Your render function should look like this:
render() {
const placesOutput = this.state.places.map(place => {
return <Text>{place}</Text> //Added return statement
})
return (
<View style={styles.container}>
<View style={styles.inputContainer}>
<TextInput
placeholder="Type something"
style={styles.placeInput}
value={this.state.placeName}
onChangeText={this.placeNameChangedHandler}/>
<Button
style={styles.placeButton}
onPress={this.placeSubmitHandler}
title="Add"/>
</View>
<View>
{this.placesOutput}
</View>
</View>
);
}
All I did was add a return statement in your this.state.places.map() function.

Shoutem fetch data not displaying

First I want to start by saying I am a total noob at React Native and Shoutem. I am not sure if I should write Shoutem or React Native in the subject of my questions, but here is my problem.
I have two screens:
Screen1.js
Screen2.js
Screen1 displays a list of items returned from a fetch. Once I click on the item, it will open the second screen which is the details screen.
I am passing data from screen1 to screen2. In screen2 I need to make another fetch call for different data, but it does not work. I am doing exactly the same thing on both screens.
Here is my code for Screen1:
import React, {
Component
} from 'react';
import {
ActivityIndicator,
TouchableOpacity
} from 'react-native';
import {
View,
ListView,
Text,
Image,
Tile,
Title,
Subtitle,
Overlay,
Screen
} from '#shoutem/ui';
import {
NavigationBar
} from '#shoutem/ui/navigation';
import {
navigateTo
} from '#shoutem/core/navigation';
import {
ext
} from '../extension';
import {
connect
} from 'react-redux';
export class List extends Component {
constructor(props) {
super(props);
this.renderRow = this.renderRow.bind(this);
this.state = {
isLoading: true,
content: null,
}
}
componentDidMount() {
return fetch('https://www.cannabisreports.com/api/v1.0/strains').then((response) => response.json()).then((responseData) => {
this.setState({
isLoading: false,
content: responseData
});
}).done();
}
renderRow(rowData) {
const { navigateTo } = this.props;
return (
//<Text>{rowData.name}, {rowData.createdAt.datetime}</Text>
<TouchableOpacity onPress={() => navigateTo({
screen: ext('Strain'),
props: { rowData }
})}>
<Image styleName="large-banner" source={{ uri: rowData.image &&
rowData.image ? rowData.image : undefined }}>
<Tile>
<Title>{rowData.name}</Title>
<Subtitle>none</Subtitle>
</Tile>
</Image>
</TouchableOpacity>
);
}
render() {
if (this.state.isLoading) {
return (
<View style={{flex: 1, paddingTop: 20}}>
<ActivityIndicator />
</View>
);
}
return (
<View style={{flex: 1, paddingTop: 20}}>
<ListView
data={this.state.content.data}
renderRow={rowData => this.renderRow(rowData)}
/>
</View>
);
}
}
// connect screen to redux store
export default connect(
undefined,
{ navigateTo }
)(List);
I am passing rowData to Screen2. I then need to make another fetch calling using data from rowData as it is a path parameter needed for the API call in Screen2.
So basically I need to make a fetch call in Screen2 like this:
fetch('https://mywebsite.com/'+rowData.something+'/myotherdata')
.then((response) => response.json())
.then((responseJson) => {
this.setState({
content: responseJson.data
})
})
.catch((error) => {
console.error(error);
});
Here is my code for screen2:
export default class Strain extends Component {
constructor(props) {
super(props);
this.state = {
content: null,
}
}
componentDidMount() {
return fetch('https://mywebsite.com/'+rowData.something+'/myotherdata')
.then((response) => response.json())
.then((responseJson) => {
this.setState({
content: responseJson.data
})
})
.catch((error) => {
console.error(error);
});
}
renderRow(dataContent) {
return (
<Text>{dataContent.name}</Text>
// This does not work either -> <Text>{dataContent}</Text>
);
}
render() {
const { rowData } = this.props; //This is coming from screen1.js
return (
<ScrollView style = {{marginTop:-70}}>
<Image styleName="large-portrait" source={{ uri: rowData.image &&
rowData.image ? rowData.image : undefined }}>
<Tile>
<Title>{rowData.name}</Title>
<Subtitle>{rowData.createdAt.datetime}</Subtitle>
</Tile>
</Image>
<Row>
<Text>Seed Company: {rowData.seedCompany.name}</Text>
</Row>
<Divider styleName="line" />
<Row>
<Icon name="laptop" />
<View styleName="vertical">
<Subtitle>Visit webpage</Subtitle>
<Text>{rowData.url}</Text>
</View>
<Icon name="right-arrow" />
</Row>
<Divider styleName="line" />
<View style={{flex: 1, paddingTop: 20}}>
<ListView
data={content}
renderRow={dataContent => this.renderRow(dataContent)}
/>
</View>
<Divider styleName="line" />
</ScrollView>
);
}
}
My fetch URL returns data like this:
{
data: {
name: "7.5833",
another_name: "8.6000",
different_name: "5.7500",
}
}
This only returns one data object like what you see above.
When I run the code I get this error:
null is not an object (evaluating 'Object.keys(e[t])')
Please let me know if you need me to provide more info.
I have tried so many different things and nothing seems to work so I am in need of some help. What am I doing wrong?
Not sure why this works but it does.
I used a function to fetch my data and then call the function in componentDidMount like this:
getData() {
return fetch('https://mywebsite.com/myotherdata')
.then((response) => response.json())
.then((responseJson) => {
this.setState({
data: responseJson.data
});
})
.catch((error) => {
console.error(error);
});
}
componentDidMount() {
this.getData();
}
Then to get the values from the JSON response I am doing this:
this.state.data.name
I am having another issue, but I will create another question.

In React-native, how to handle checkbox in Listview?

In my react-native app, I am trying to show my contact details with checkboxes for selecting.
Here is my code:
<ListView
dataSource={this.state.dataSource}
renderRow={(rowData, sectionID, rowID) => (
<TouchableHighlight onPress={() => this.goRideDetails(rowData)}>
<Text style={styles.rideHeader}>{rowData.name} </Text>
<CheckBox
checked={this.state.checked}
onCheckBoxPressed={() =>
this.setState({ checked: !this.state.checked })
}
/>
</TouchableHighlight>
)}
/>
In my view checkbox is displaying on every row, but not working.
Any one can help me. Thanks in advance.
You can easily do this with component separation. Please, take a look here:
export default class ContactList extends Component {
static propTypes = {
contacts: React.PropTypes.array,
}
static defaultProps = {
contacts: [],
}
constructor(){
super();
this._renderRow = this._renderRow.bind(this);
}
_renderRow(rowData,sectionID,rowID) {
return <Contact info={ rowData } />;
}
render() {
return (
<ListView
dataSource={ this.props.contacts }
renderRow={ this._renderRow }
/>
);
}
}
export class ContactList extends Component {
static propTypes = {
info: React.PropTypes.object.isRequired,
}
constructor(){
super();
this.goRideDetails = this.goRideDetails.bind(this);
this.setChecked = this.setChecked.bind(this);
}
goRideDetails() {
//your logic here
}
setChecked() {
this.props.info.checked = !this.props.info.checked; //will be much better to do it with redux and action creators
}
render() {
return (
<TouchableHighlight onPress={ this.goRideDetails }>
<Text style={ styles.rideHeader }>{this.props.info.name} </Text>
<CheckBox checked={ this.props.info.checked } onCheckBoxPressed={ this.setChecked } />
</TouchableHighlight>
);
}
}
After that you can simply call:
<ContactList contacts={this.state.dataSource} />
in your jsx and voila.
Important note: Do not use array functions inside your jsx code blocks.
Important note 2: Try to start using redux or flux for storing state of your application. It will be provide much better code design.
Hope, it will help.
import React , {Component} from 'react'
import {
Text,
View,
ListView,
StyleSheet,
TouchableOpacity,
Image,
} from 'react-native'
import CheckBox from 'react-native-checkbox'
var Folder = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2})
var folder = '' ////// all the new folder
var check_folder = [] ////// all the check box conditions
class ApproveContent extends Component {
///////// all the upper thing that are global variable for this script is has same value as that of the state the only reason we are using this because of the layout update //////////
state={
folder:[],
data:[],
check:[]/////// this need to do just to upadte the layout of the check box
}
render(){
return(
<View style = {{flex:1,backgroundColor:'white',alignItems:'center'}}>
<ListView
dataSource={Folder.cloneWithRows(this.state.folder)}
renderRow={(rowData,rowID,sectionID) => <View style = {{ alignItems: 'center',margin:5}}>
<TouchableOpacity style = {{width:Dimension.ScreenWidth/1.2,height:Dimension.ScreenHeight/6,flexDirection: 'row',alignItems:'center'}}
onPress={() => {}}>
<CheckBox
label=''
labelBefore={false}
checked={this.state.check[sectionID]}
checkboxStyle = {{marginLeft: 20}}
onChange={(checked) => {
this.setState({
check:!this.state.check
})
if(check_folder[sectionID] == false){
check_folder[sectionID] = true
this.setState({
check:check_folder// has to do this because we cant change the single element in the array
})
}else{
check_folder[sectionID] = false
this.setState({
check:check_folder// has to do this because we cant change the single element in the array
})
}
console.log(check_folder)a
}}
/>
</TouchableOpacity>
</View>
}
/>
</View>
)}
}
export default ApproveContent
const style = StyleSheet.create({
TextStyle:{
fontFamily: 'Roboto-Bold',
fontSize:15,
},
approveButton: {
bottom:0,
left:0,
alignItems: 'center',
}
})