How to show images dynamically with react native - javascript

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")]

Related

Loading Images sequentially in React Native

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;

How to render a single post from my post list in my app built with React Native and Apollo?

So I am trying to build an app similar to a blog. And I am not sure how to render the single post that has been chosen to read.
<TouchableWithoutFeedback onPress={ () => this.props.navigation.navigate('Unique')}>
<Text style={styles.titleStyle}>{post.title}</Text>
</TouchableWithoutFeedback>
^This is the event that navigates me to the post I have clicked (not sure how to pass its props or id)
And this is the component I am redirected to:
class Page extends Component {
render(){
return(
<ScrollView>
<Query query={UniqueNews}>
{({data, loading})=> {
if(loading) return <Text>Loading</Text>;
const {posts} = data;
return posts.map(post=>
<OneNews key={post.id} post={post} />
)
}}
</Query>
</ScrollView>
)
}
}
const UniqueNews = gql `query{
posts{
title
author
date
id
body
image{
url
}
}
}
`
OneNews =
<View style={styles.containerStyle}>
<View style={styles.cardSectionStyle}>
<Text>{title}</Text>
<Text>{author}</Text>
<Text>{body}</Text>
</View>
</View>
When I render or navigate to that screen, every post is shown instead of the one I have tapped on. How do I set this up? My expected output is to just see one of the posts (being the chosen one by tapping on it) and render it. Thanks
I found a way to solve this:
First I will pass a param while navigating to the Unique component :
<TouchableWithoutFeedback onPress={ () => this.props.navigation.navigate('Unique', {id: post.id})}>
<Text style={styles.titleStyle}>{post.title}</Text>
</TouchableWithoutFeedback>
After that, I will simply make a conditional render inside that Unique component:
<ScrollView>
<Query query={singleNews}>
{({data, loading})=> {
if(loading) return <Text>Loading</Text>;
const {posts} = data;
return posts.map(post=>
{if(this.props.navigation.state.params.id===post.id)
return <View>
<OneNews key={post.id} post={post} />
</View>}
)}}
</Query>
</ScrollView>
And that's it.

Render button in child component only on certain screen

Im using a flat list on 2 different screens.
On the EventListScreen:
this is the main screen and should display all events.
and on the 2nd page UserProfile.js this page should only display that users events.
in both flat lists I'm using a pure component stored in a seperate class, to where the flat lists are i.e
My Question is, I want to display an "Edit" button on the Event.js child component only if the User is on the
UserProfileScreen.js
I have looked up a lot of example but cant really find any that show how to do it
with a child pure component like I'm doing.
Any Help would be greatly appreciated! Thank you
EventListScreen.js
<FlatList
data={this.state.events}
// Get the item data by referencing as a new function to it
renderItem={({item}) =>
<Event
openEventDetail={() => this.openEventDetail(item)}
{...item}
/>}
/>
UserProfileScreen.js
<FlatList
data={this.state.events}
// Get the item data by referencing as a new function to it
renderItem={({item}) =>
<Event
openEventDetail={() => this.openEventDetail(item)}
openEditEvent={() => this.openEditEvent(item)}
{...item}
/>}
/>
Event.js
export default class Event extends Component {
render() {
return (
<Card>
<CardSection>
<Text>{this.props.eventName}</Text>
//I want this button to be displayed only if user is viewing
//from the UserProfile.js
<Button onPress={() =>this.props.openEditEvent()}>
{this.props.displayButton}
</Button>
</CardSection>
<TouchableOpacity
onPress={() => this.props.openEventDetail()}
>
}
You don't need additional properties.
We can assume that the "Edit" button should be available when openEditEvent prop is defined.
Condition in event (using convertion to bool, false for undefined):
<CardSection>
<Text>{this.props.eventName}</Text>
{!!this.props.openEditEvent &&
<Button onPress={() =>this.props.openEditEvent()}>
{this.props.displayButton}
</Button>
}
</CardSection>
Use propTypes to define openEditEvent prop as a function, optional (not required).
If I understand your problem correctly an option to solve this problem would be to pass a boolean "showable prop" to show the edit button only when required:
EventListScreen.js (Stays the same, we don't show the edit button here)
<FlatList
data={this.state.events}
// Get the item data by referencing as a new function to it
renderItem={({item}) =>
<Event
openEventDetail={() => this.openEventDetail(item)}
{...item}
/>}
/>
UserProfileScreen.js (we add the shouldShowEditButton prop to event in order to show the button)
<FlatList
data={this.state.events}
// Get the item data by referencing as a new function to it
renderItem={({item}) =>
<Event
openEventDetail={() => this.openEventDetail(item)}
openEditEvent={() => this.openEditEvent(item)}
shouldShowEditButton
{...item}
/>}
/>
Event.js (We add some propTypes and defaultProps to handle the new prop, it won't show the edit button if not specified)
export default class Event extends Component {
render() {
return (
<Card>
<CardSection>
<Text>{this.props.eventName}</Text>
//I want this button to be displayed only if user is viewing
//from the UserProfile.js
{this.props.shouldShowEditButton && <Button onPress={() =>this.props.openEditEvent()}>
{this.props.displayButton}
</Button>}
</CardSection>
<TouchableOpacity
onPress={() => this.props.openEventDetail()}
>
...
...
);
...
}
}
// We add some default propTypes and definitions
Event.propTypes = {
shouldShowEditButton: PropTypes.bool
};
Event.defaultProps = {
shouldShowEditButton: false
};
In this way you're only showing the edit button for the components that have the prop shouldShowEditButton defined, and because its default value is defined as false, the components that don't have the property will behave in the same way they were before.

Adding dynamic local images in a react native listview

I'm trying to load some local images in a simple app that I'm making using react native.
I'm following the guide here - https://facebook.github.io/react-native/docs/images.html
Unfortunately, that guide says the following:
Note that in order for this to work, the image name in require has to be known statically.
Which is why my code isn't working when I try to render rows in a list view:
renderRow(chat){
return (
<View style={ styles.row }>
<Image
source={require(`../images/${chat.image}`)} //this is the part that's not working. It can't require a dynamic path
style={ styles.cellImage }
/>
</View>
);
}
How can I achieve this? Or can you suggest another way that will work?
It's quirky and odd, but if you have a set number of options for the images (which makes sense if the images are living in the project) then you can return the statically required image based on a given condition.
getSource() {
if (condition) {
return require( '../images/option-one' );
} else {
return require( '../images/default-option' );
}
}
renderRow(chat){
return (
<View style={ styles.row }>
<Image
source={ this.getSource() }
style={ styles.cellImage }
/>
</View>
);
}

Creating custom components

I've been trying to get a UITableView equivalent in React-Native. The screen looks like this (work in progress):
The code for this is quite primitive at the moment:
class SettingsView extends Component {
render() {
return(
<View style={styles.container}>
//more View code
//example of a cell
<TouchableHighlight style={styles.tableViewCell}>
<Text style={styles.cellLabel}>Log Out</Text>
</TouchableHighlight>
</View>
);
}
}
It works fine, I've been trying to create an accessory - an > indicator - for all my cells. Whilst doing that, I stumbled upon a way to create custom component via this code:
class TableViewCell extends Component {
render() {
return (
<TouchableHighlight style={styles.tableViewCell}>
<Text style={styles.cellLabel}>Log Out</Text>
</TouchableHighlight>
);
}
}
Next, I replaced the initial block of code and I came out with this:
class SettingsView extends Component {
render() {
return(
<View style={styles.container}>
//more View code
//example of a cell
//Replaces the previous block of TouchableHighlight
<TableViewCell/>
</View>
);
}
}
Wow. To a native Swift developer that has barely any experience with HTML and JavaScript, this is amazing. I immediately went on quest in an attempt to find out how I might make this reusable. Currently, TableViewCell is hardcoded to have the text "Log Out". Ideally I want to be able to supply the text as an argument for the constructor. This is where I got stuck. Here's what I've tried so far:
Use getAttribute to see if I could extract an attribute that I would pass in whilst declaring the TableViewCell tag.
//when declaring a cell on the screen's render
<TableViewCell titleText="About"/>
//tableViewCell component
render() {
return(
<TouchableHighlight ...>
<Text>{this.getAttribute("titleText")}</Text>
</TouchableHighlight>
);
}
I couldn't get a reference to the titleText that I've declared as part of the tag. Any ideas?
I could be wrong, but I'm pretty sure this is what you need. You can pass properties to components and receive them as this.props:
// when declaring a cell on the screen's render
<TableViewCell titleText="About"/>
// tableViewCell component
render() {
return(
<TouchableHighlight ...>
<Text>{this.props.titleText}</Text>
</TouchableHighlight>
);
}

Categories