Suppose I'm making a gallery-like app where you can view a list of images in a long strip. This seems simple enough but I'm encountering a problem where these images are loaded at the same time and (my guess) is that these images are behind some sort of DDoS protection service which prevents me from loading them all at the same time (It gave me 403 errors).
So my solution would be to load them one by one sequentially, however I'm not sure how this could be done in React as I'm quite new to React/React Native in general and communications between sibling components and/or child-parent aren't very straight forward. I've read up on Redux and could this be the solution? - But I've still not got the clear picture on how this can be done either.
For example this is what I have which isn't working
class Gallery extends React.Component {
...
renderPage(page) { //Each Images
return (<Page image={page.item} />)
}
...
render () {
return (
<Container>
<Header />
<Content>
{ this.state.isLoading ? <Spinner /> : (
// this loads all images at the same time which causes error
<FlatList
initialNumToRender={2}
data={this.state.pageArray}
renderItem={this.renderPage}
keyExtractor={(item, index) => index}
/>
)}
</Content>
</Container>
);
}
}
export default Gallery;
I have tried this on my own, Maybe it will help you a bit.
import React, {Component} from 'react';
import {Text, FlatList} from 'react-native';
class Gallery extends Component {
state = {
pageArr: [{name: 'abc'}, {name: 'def'}, {name: 'ghi'}],
isLoading: false,
};
renderPage(page) {
return <Text>{page.name}</Text>;
}
render() {
const {isLoading, pageArr} = this.state;
return isLoading ? (
<Spinner />
) : (
<FlatList
data={pageArr}
renderItem={({item}) => this.renderPage(item)}
keyExtractor={(item, index) => index}
/>
);
}
}
export default Gallery;
Related
I have an listing app where users can add items for multiple categories, when they want to add new record, there are 3 related screens with this particular feature. All of those screens have <Header/> component, so i thought HoC would be nice here so that i can reuse it across 3 screens.
However, i could not accomplish it.
Here is what i tried so far:
This is my HoC class
import React, { Component } from 'react';
import { View, StyleSheet, Text, StatusBar } from 'react-native';
import Header from '../components/Header';
const NewAd = (WrappedComponent) => {
class NewAdHoc extends Component {
handleBackPress = () => {
this.props.navigation.navigate('Home');
StatusBar.setBarStyle('dark-content', true);
}
render() {
const {contentText, children} = this.props
return (
<View style={styles.container}>
<Header
headerText={'Yeni ilan ekle'}
onPress={this.handleBackPress}
/>
<View style={styles.contentContainer}>
<Text style={styles.contentHeader}>{contentText}</Text>
<WrappedComponent/>
</View>
</View>
);
}
}
return NewAdHoc;
}
this is my screen:
class NewAdScreen extends Component {
render() {
const Content = () => {
return (
<View style={styles.flatListContainer}>
<ListViewItem />
</View>
);
}
return (
NewAdHoc(Content)
)
}
}
after that i am getting error
TypeError: (0 , _NewAdHoc.NewAdHoc) is not a function(…)
and i have no idea how can i fix it because this is my first time using hocs on a react-native app. I have looked why this error is popping and they suggest import components in this way:
import {NewAdHoc} from '../hocs/NewAdHoc';
but even this is not solved it.
any help will be appreciated, thanks.
The main purpose of a HOC is to encapsulate and reuse stateful logic across components. Since you are just reusing some jsx and injecting nothing in WrappedComponent you should be using a regular component here:
const NewAd = ({ contentText, children }) => {
handleBackPress = () => {
this.props.navigation.navigate('Home');
StatusBar.setBarStyle('dark-content', true);
}
return (
<View style={styles.container}>
<Header
headerText={'Yeni ilan ekle'}
onPress={this.handleBackPress}
/>
<View style={styles.contentContainer}>
<Text style={styles.contentHeader}>{contentText}</Text>
{children}
</View>
</View>
);
}
And use it like this
return(
<>
<NewAd>
<Screen1 />
</NewAd>
<NewAd>
<Screen2 />
</NewAd>
<NewAd>
<Screen3 />
</NewAd>
</>
)
I am facing an issue where a component Question is not correctly receiving data from my react-redux store. When the component is initially rendered, it receives the desired data from the store with no issue. The problem arises when the question becomes a sub-question - when this is the case, the data from the store is not retrieved and is instead undefined, despite it being the same code. The sub-question code can be seen below where it renders a new Question component within the same Question component. This recursively rendered component does not receive the correct state.
The initial render code snippet is as follows, placed in an outside component from Question:
<div style={{ paddingRight: '8px' }}>
{data.Question.map(question => (
<Question question={question} key={question.ID} />
))}
</div>
The Question component is as follows:
import React from 'react';
import Typography from '#material-ui/core/Typography';
import Grid from '#material-ui/core/Grid';
import FlexibleInput from '../Inputs/FlexibleInput';
import {checkConditionals} from '../../dataHelper';
import { connect } from 'react-redux';
const mapStateToProps = (state, ownProps) => {
return {
answers: state.answers,
activeStep: state.stepper
}
}
const mapDispatchToProps = { }
class Question extends React.Component {
getStyle = () => {
if (this.props.subQuestion) return {paddingLeft: '24px', paddingRight: '-12px'}
return {}
}
render() {
const question = this.props.question;
console.log(this.props.activeStep, question.ID, this.props.answers);
return (
<React.Fragment>
{checkConditionals(question, this.props.answers) ? (
<div style={this.getStyle()}>
{/*Grid is used to placed the question and the possible answers on the same line*/}
<Grid container spacing={2}>
<Grid item> {/*Grid item for the question's prompt*/}
<Typography style={{padding: '12px', fontSize: '1rem'}}>
{question.Description}
</Typography>
</Grid>
<Grid item> {/*Grid item for the question's answer options*/}
<FlexibleInput obj={question}/>
</Grid>
</Grid>
{/*Display a question's sub-questions if they exist, mapping each sub question in the array to a new section*/
question.SubQuestion ? (
question.SubQuestion.map(subQuestion => (
<Question question={subQuestion} key={subQuestion.ID} subQuestion={true}/>
))
) : (
<React.Fragment></React.Fragment>
) }
</div>
) : (
<React.Fragment></React.Fragment>
)}
</React.Fragment>
);
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Question);
Question, on its own, does not have access to the store. By using connect you create a new component, and that component has access to the store. You do use connect at the end of the file, and you export the connected component, so as far as the rest of your codebase is concerned everything is fine. But inside this particular file, any references to <Question> are referring to the unconnected component.
Perhaps do this:
class Question extends React.Component {
// ... later, in render
question.SubQuestion.map(subQuestion => (
<ConnectedQuestion question={subQuestion} key={subQuestion.ID} subQuestion={true}/>
))
}
const ConnectedQuestion = connect(
mapStateToProps,
mapDispatchToProps
)(Question);
export default ConnectedQuestion;
creating an application in react-native running into this problem. basically I should create a page that prints the data of the user currently logged in to the database (firebase). I managed to create a sort of leaderboard that prints all users with data, on another page, but I can not figure out where I was wrong. can someone help me?
https://snack.expo.io/#khal_d/proj-p-3
import React, { Component } from 'react';
import { View, TouchableOpacity, StyleSheet, Button, Text, ScrollView, ListItem } from 'react-native';
import { Input, Card} from 'react-native-elements';
import * as firebase from 'firebase';
export default class User extends Component {
static navigationOptions = {
title: 'UserInfo',
};
state = {
data: [],
};
// Controllare qui
componentDidMount(){
//leggere array dal db
const currentUID = firebase.auth().currentUser.uid;
const path ="/users/" + currentUID;
const users = firebase.database().ref(path);
users.on("value", snap => {
console.log("log di snap" + snap);
//ciclo
var elenco = [];
snap.forEach(child => {
elenco.push({
name: child.val().name,
surname: child.val().surname,
email: child.val().email,
image: child.val().image,
})
});
console.log("altro log finale" + elenco);
this.setState({data:elenco})
});
}
// controllare fino a qua
render() {
return (
<ScrollView>
<View>
<Card> //fix evertything all'interno di card
{
this.state.data.map((l, i) => (
<ListItem
key={i}
leftAvatar={{ source: { uri: l.image } }}
title={l.name}
subtitle={l.surname}
/>
))
}
</Card>
</View>
</ScrollView>
);
}
}
I think the issue is because of the comment. In JSX, comment with // doesn't work. It will be treated as a text.
You have to change your comments like below which will fix your issue
{/* fix evertything all'interno di card */}
Just delete the comments and extra { }from your code in render() or use them as below. In JSX you cannot have // in render():
render() {
return (
<ScrollView>
<View>
<Card>
{ this.state.data &&
this.state.data.map((l, i) => (
<ListItem
key={i}
leftAvatar={{ source: { uri: l.image } }}
title={l.name}
subtitle={l.surname}
/>
))
}
</Card>
</View>
</ScrollView>
);
}
}
iOS has no problem with extra syntaxes in render(), but in android it will show that error.
Also because of asynchronous problem of setState, you will have and error of undefined is not an object. So it is better to have {this.state.data && condition on your ListItem. I hope I could help :)
ListItem should be imported from react-native-elements and not from react-native.
I want to build an album app with react native, the main idea is to use a sectionList and show the data in an array like this:
class CustomImage extends Component{
render(){
return(
<View>
<Image style={styles.Img} source={this.props.imageName} />
<Text style={styles.text}>{this.props.name}</Text>
</View>
);
}
}
export default class DisplayAnImage extends Component {
render() {
return (
<View>
<SectionList
sections={[
{titile: 'small soldier', data: ["./gifs/2.gif", "./gifs/3.gif", "./gifs/4.gif"]}
]}
renderItem={({item}) => <CustomImage name={item} fromWeb={false} imageName={require(item)} />}
renderSectionHeader={({section}) => <Text style={styles.sectionHeader}>{section.title}</Text>}
/>
</View>
);
}
}
Above code was indented to make it work inspirited by this answer
I have already put the gifs folder under the project folder, and if I using a static string as source={require(url)} in the Image component, things will work, but when the url came out of an array items iteration, it will not work.
How can I make this work with react native?
EDIT
Don't know if I could edit to make it more specificly, the really thing I want to do is to use a generated array like this:
function numberRange(start, end){
return new Array(end-start).fill().map((d,i) => {
var url = "./gifs/" + ( i+start) + ".gif";
return require(url)
});
}
export default class DisplayAnImage extends Component {
render() {
return (
<View>
<SectionList
sections={[
{title: 'small soldier', data: numberRange(2,30)},
{title: 'small soldier', data: numberRange(31,60)}
]}
renderItem={({item}) => <CustomImage name={item} fromWeb={false} imageName={item} />}
renderSectionHeader={({section}) => <Text style={styles.sectionHeader}>{section.title}</Text>}
/>
</View>
);
}
}
Don't know if have a way to use this array generator to make an array or I have to enter the path one by one on bare hand :-(
require is a javascript keyword with a preload nature.
And, images will not related to path at runtime --- provide a path to it will not get anything. it becomes bundle resources, so with console.log you can only see resource token related to bundle ex: _1 _2.
It will not work even change require("./gifs/1.gif") to require("./gifs/"+"1.gif").
Try this:
data: [require("./gifs/2.gif"), require("./gifs/3.gif"), require("./gifs/4.gif")]
I'm developing an app in react-native and i'm confronted to a problem:
I want that when i click on a button ( or directly the View where the button is ) a Webview is shown on screen and open the link directly passed in parameters.
But nothing happens.
Here is my code:
return (
<ScrollView style={[style.case1]} refreshControl={<RefreshControl refreshing={this.state.refreshing} onRefresh={this.handleRefresh} />} >
{
this.state.orders.map(function (order) {
let a = _this.getPosts(order);
let Final = _this.splitString(a.message," ");
return (
<View style={[style.case2]} key={a.message} >
<Couleur couleur={Final[4]} />
<Text style={[style.Nom]} >Nom : {Final[1]}</Text>
<Text style={[style.Nom]} >Numéro de build : {Final[2]}</Text>
<Button onPress={<Web url={Final[3]} />} title="Click"/>
</View>
);
})}
</ScrollView>
);
And the WebView class :
import React, {Component} from "react";
import {WebView} from "react-native";
export default class Web extends Component {
constructor(props){
super(props);
}
render() {
let uri = this.props.url;
return(
<WebView
ref={(ref) => {this.webview = ref;}}
source={{uri}}
style={{marginTop:20}}
/>
)
}
}
And i got this error :
"Object is not a function(evaluating 'this.props.onPress(e)')
I would be very happy if someone help me ! :)
The onPress action needs to be a function that does something. Right now, you are setting the action to a component and react doesn't know what to do with that.
Without some kind of navigation library controlling your views, you could do something like have the onPress set some state that controls a part of the render function that either shows your existing page or the new 'Web' component.
So make onPress like:
onPress={e => this.setState({showWebPart:true})}
Then in your current render function you could have a ternary like:
{this.state.showWebPart ? <Web url={Final[3]} /> : ..current render stuff}