How to render a loader until data is fetched in React Native - javascript

I am fetching data through an async request. I know that I need to wait for the api request to complete before displaying the data. Unfortunately, I'm not sure how to create a loader to wait for the data to load.I am new to react, so if I could also get help with implementing it as well, that would be fantastic! Here is my current code:
import React, { Component, PropTypes } from 'react';
import { View, Text, ListView, StyleSheet, TouchableHighlight} from 'react- native';
import Header from '../Components/Header';
import Api from '../Utility/Api';
export default class CalendarPage extends Component {
constructor(props) {
super(props);
}
async componentWillMount() { this.setState(
{data: await Api.getDates()},
)
}
render() {
return (
<View style={{flex: 1}}>
<Header pageName="Calendar" navigator={this.props.navigator}/>
<View style = {{flex:9}}>
<View>
{ this.state.data.days[0].items.map((item) => (
<View>
<Text>{item.summary}</Text>
<Text>{item.start.dateTime}</Text>
<Text>{item.description}</Text>
</View>
))}
</View>
</View>
</View>
);
}
}

A simple example using ActivityIndicator -
import ActivityIndicator
import { View, Text, ListView, StyleSheet, TouchableHighlight, ActivityIndicator} from 'react- native';
set data state to null
constructor(props) {
super(props);
this.state = {
data: null
}
}
do conditional rendering
render() {
if (!this.state.data) {
return (
<ActivityIndicator
animating={true}
style={styles.indicator}
size="large"
/>
);
}
return (
<View style={{flex: 1}}>
<Header pageName="Calendar" navigator={this.props.navigator}/>
....
....
</View>
);
}
}
indicator style
const styles = StyleSheet.create({
indicator: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
height: 80
}
});

Although solution proposed by #vinayr works fine but user will still be able to click on screen and perform some action even while loader is being shown which can lead to crash.
One solution is wrap loader inside a Modal.
import React, { Component } from 'react';
import {
StyleSheet,
View,
Modal,
ActivityIndicator,
} from 'react-native';
const styles = StyleSheet.create({
modalBackground: {
flex: 1,
alignItems: 'center',
flexDirection: 'column',
justifyContent: 'space-around',
backgroundColor: '#00000040',
},
activityIndicatorHolder: {
backgroundColor: '#FFFFFF',
height: 100,
width: 100,
borderRadius: 10,
display: 'flex',
alignItems: 'center',
justifyContent: 'space-around',
},
});
const SmartLoader = (props) => {
const {
isLoading,
...attributes
} = props;
return (
<Modal
transparent
animationType={'none'}
visible={isLoading}
onRequestClose={() => { console.log('Noop'); }}
>
<View style={styles.modalBackground}>
<View style={styles.activityIndicatorHolder}>
<ActivityIndicator
animating={isLoading}
size="large"
/>
</View>
</View>
</Modal>
);
};
export default SmartLoader;
After that you can use it anywhere in your component, user will not be able to perform any action till loader is finished ( made hidden based on flag)

Related

TypeError: render is not a function. (In 'render(newValue)', 'render' is an instance of Object)

import{ React, useContext} from 'react';
import { Button, View, Text } from 'react-native';
import { NavigationContainer, useNavigation ,useRoute } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
import { LoginContexts } from '../Contexts/LoginContexts';
function Screen2() {
const {getEmail} = useContext(LoginContexts);
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<LoginContexts.Consumer >
<Text>{getEmail}</Text>
</LoginContexts.Consumer>
</View>
);
}
export default Screen2;
This is my consumer part is which I want to display the text which is present in getEmail in
Text View. please help to solve this problem.
The issue is that LoginContexts.Consumer component expects a render function.
Context.Consumer
<MyContext.Consumer>
{value => /* render something based on the context value */}
</MyContext.Consumer>
The useContext hook is the consumer, remove the LoginContexts.Consumer component.
function Screen2() {
const {getEmail} = useContext(LoginContexts);
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>{getEmail}</Text>
</View>
);
}

My simple react native project won't render, any suggestions?

I'm using the expo application on my phone to build a simple react native application, but I'm unable to render simple text. Is there something I'm doing wrong?
import React, { Component } from 'react';
import { StyleSheet, Text, View, Image } from 'react-native';
import bball from './assets/bball.jpeg'
export default class App extends Component {
render() {
let pic = {
uri: bball
}
return (
<View style={styles.container}>
<Text>hello, world</Text>
<Image source={{pic}} />
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
Have you tried this way?
import React, { Component } from 'react';
import { StyleSheet, Text, View, Image } from 'react-native';
export default class App extends Component {
render() {
return (
<View style={styles.container}>
<Text>hello, world</Text>
<Image source={require('./assets/bball.jpeg')} />
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
#Myers Nii-Ansah, try to import image like
const image = require('./my-icon.png');
just use directly bbal in the source of image.
<Image source={bbal} />
if you have url of any image use like this.
<Image source={{uri: "https://example.com/image.png"}} />
so you can do this way if any how you can have image url from your component state.
render() {
let pic = this.state.url ? { uri: this.state.url } : bbal
return (
<View style={styles.container}>
<Text>hello, world</Text>
<Image source={pic} />
</View>
)
}

Conditional Rendering of child elements in React

I am trying to write a reusable Header Component in React-Native. I want to write it in a ways that the left and right button can be passed as child components. To know where to render which button I want to pass a prop like rightIcon or leftIcon. However I don't know how to access these props.
This is my App.js file
import React from 'react';
import {StyleSheet, TouchableHighlight, View} from 'react-native';
import Header from "./src/Header";
import {Ionicons} from '#expo/vector-icons';
export default class App extends React.Component {
render() {
return (
<View style={styles.container}>
<Header headerText={"Barcode Scanner"}>
<TouchableHighlight righticon>
<Ionicons name="md-barcode" size={36} color="white"></Ionicons>
</TouchableHighlight>
</Header>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1
},
});
And this is the Header Component
import React from 'react';
import {Text, View} from 'react-native';
export default class Header extends React.Component {
render() {
const {textStyle, viewStyle, rightButton} = styles;
return (
<View style={viewStyle}>
<Text style={textStyle}>{this.props.headerText}</Text>
<View style={rightButton}>
{this.renderRightChild()}
</View>
</View>
);
}
renderRightChild = () => {
console.log("Check if rightIcon Prop is set");
}
}
const styles = {
viewStyle: {
backgroundColor: '#5161b8',
justifyContent: 'center',
alignItems: 'center',
height: 80,
paddingTop: 25,
shadowColor: '#000',
shadowOffset: {width: 0, height: 2},
shadowOpacity: 0.2,
elevation: 2,
position: 'relative'
},
textStyle: {
color: '#fff',
fontSize: 20
},
rightButton: {
position: 'absolute',
top:
35,
right:
20
}
}
;
I already tried to use React.Children.toArray but this always throws an error that the request entity is too large.
Thanks for all the answers
I guess you can always use a render prop that way you can not only decide whether to render left/right icon component but the component rendering the icon does not even have to know what to render:
The term “render prop” refers to a simple technique for sharing code
between React components using a prop whose value is a function.
return (
<View style={styles.container}>
<Header
headerText={"Barcode Scanner"}
renderRightIcon={() => (
<TouchableHighlight righticon>
<Ionicons name="md-barcode" size={36} color="white" />
</TouchableHighlight>
)}
/>
</View>
);
Then you can use call the right icon as a function:
return (
<View style={viewStyle}>
<Text style={textStyle}>{this.props.headerText}</Text>
{renderLeftIcon && (
<View style={leftButton}>
{renderLeftIcon()}
</View>)
}
{renderRightIcon && (
<View style={rightButton}>
{renderRightIcon()}
</View>)
}
</View>
);
You render both components, the right and left and you put an if condition inside state.
Header Component render method
render() {
const { leftOrRight } = this.props // right - true, left - false
return(
...
{ leftOrRight ? <RightIcon /> : <LeftIcon />}
);
}
Inside Component that calls Header
import Header from './somepath ...';
class Something extends React.Component {
this.state = { leftOrRight }
render() {
return(
<Header leftOrRight = {this.state.LeftOrRight}/>
);
}
}
You could have a function that sets leftOrRight in your parent class
One way to do this is write a Header Component and pass all the things, as props, which you can then access them in Header Components Props like..
<Header title="HeaderTitle"
leftButtonTitle="LeftButton"
rightButton={canBeAObjectWithSomeInfo}
leftButtonClick={handleClick} />
and then in your header component(can be class or a function)
const Header = ({}) => (
<View>
<View onPress={this.props.handleClick}>{this.props.leftButton}</View>
<View>{this.props.title}</View>
<View onPress={this.props.handleRightClick}>{this.props.rightButton}</View>
</View>
)
something like this you can have and then you can design header accordingly

How to navigate page with React Native

I have a component for listing items, I want to add the function that can go to a different page, and that page has the detail about that item. Currently, this is my code for listing items.
import React, { Component } from 'react';
import { ScrollView } from 'react-native';
import axios from 'axios';
import CarDetail from './CarDetail';
const API_URL = 'http://localhost:3000';
class CarList extends Component {
state = { cars: [] };
componentWillMount() {
console.log('Mount');
axios.get(`${API_URL}/cars`)
.then(response => this.setState({ cars: response.data.cars }));
}
renderCars() {
return this.state.cars.map(car => <CarDetail key={car.id} car={car} />
);
}
render() {
console.log(this.state.cars);
return (
<ScrollView>
{this.renderCars()}
</ScrollView>
);
}
}
export default CarList;
and this is the code for describing items
import React from 'react';
import { Text, View, Image } from 'react-native';
import { Actions } from 'react-native-router-flux';
import Card from '../material/Card';
import CardSection from '../material/CardSection';
const CarDetail = ({ car }) => {
const imageURI = 'https://yt3.ggpht.com/-HwO-2lhD4Co/AAAAAAAAAAI/AAAAAAAAAAA/p9WjzQD2-hU/s900-c-k-no-mo-rj-c0xffffff/photo.jpg';
const { make, model } = car;
function showCarDetail() {
Actions.showCar();
}
return (
<Card>
<CardSection>
<View style={styles.containerStyle}>
<Image
style={styles.imageStyle}
source={{ uri: imageURI }}
/>
</View>
<View style={styles.headContentStyle}>
<Text
style={styles.headerTextStyle}
onPress={showCarDetail()}
>
{make}
</Text>
<Text>{model}</Text>
</View>
</CardSection>
<CardSection>
<Image
style={styles.picStyle}
source={require('./car.jpg')}
/>
</CardSection>
</Card>
);
};
const styles = {
headContentStyle: {
flexDirection: 'column',
justifyContent: 'space-around'
},
headerTextStyle: {
fontSize: 18
},
imageStyle: {
height: 50,
width: 50
},
containerStyle: {
justifyContent: 'center',
alignItems: 'center',
marginLeft: 10,
marginRight: 10
},
picStyle: {
height: 300,
flex: 1,
width: null
}
};
export default CarDetail;
How can I change my code for that? Can anyone give me an example?
You have to use some sort of navigation component. There are many out there, but personally I use the one that is built into React Native. https://facebook.github.io/react-native/docs/navigator.html

React Native - unexpected rendering of component

I am rendering a ListView in a React Native app that I'm building and am finding that a parent View container for the row is not expanding to fit all the content within it and I'm not sure why.
See in screenshot below how the second row within the row is rendered within the margin of the row view:
Component is as follows:
import React from 'react';
import { connect } from 'react-redux';
import {
View,
Text
} from 'react-native';
import { Icon } from 'native-base';
const ListViewRow = (props) => {
const { booking } = props;
const guest = props.guests[booking.guest_id];
return (
<View style={styles.rowStyle}>
<View style={styles.leftContentStyle}>
<View>
<Text>{guest.fullname}</Text>
</View>
<View>
<Text>{booking.start_date} - {booking.end_date}</Text>
</View>
</View>
<View style={styles.rightContentStyle}>
<Icon name='ios-arrow-forward' />
</View>
</View>
);
};
const styles = {
rowStyle: {
flex: 1,
borderBottomWidth: 1,
padding: 10,
backgroundColor: '#fff',
justifyContent: 'space-between',
flexDirection: 'row',
borderColor: '#ddd',
position: 'relative'
},
leftContentStyle: {
alignItems: 'stretch'
},
rightContentStyle: {
flexDirection: 'row',
alignItems: 'center'
}
};
const mapStateToProps = state => {
return { guests: state.guests };
};
export default connect(mapStateToProps)(ListViewRow);

Categories

Resources