React native require with backticks - reactjs

I'm implementing an app and on the homescreen I have a flatlist element with inside for each item a card element.
import React, { useEffect, useContext } from 'react';
import { Text, View, StyleSheet, FlatList } from 'react-native';
import {ListItem, Card} from 'react-native-elements';
import { Context as ParkingContext } from '../context/ParkingContext';
const HomeScreen = () => {
const { state: {records}, fetchParkings } = useContext(ParkingContext);
useEffect(() => {
fetchParkings();
},[])
return (
<View style={styles.container}>
<FlatList
data={records}
keyExtractor={item => item.recordid}
renderItem={({item}) => {
return (
<Card containerStyle={styles.cardStyle} title={item.fields.description}>
<Text style={styles.textStyle}>{item.fields.address.replace(/\n/g,'')}</Text>
<Text style={styles.textStyle}>Aantal vrije plaatsen: {item.fields.availablecapacity}</Text>
</Card>
)
}}
/>
</View>
)
};
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 25
},
cardStyle: {
padding: 0,
borderRadius: 10
},
textStyle: {
flexDirection: 'row',
flex: 1,
marginBottom: 10,
marginLeft: 5
}
});
export default HomeScreen;
The card element has a property image which can be used to set a image to the card. I want to give different images to the cards by doing
image={require(`../../assets/${item.fields.description}.jpg`)}
but require doesn't let me do this with backticks and neither with a variable... is there some way to use backticks or a variable so I can have different photo's for the different cards?
My apologies if the answer is obvious but I tried a lot of things and it does not work.

Assuming you created your react app with create-react-app or you are using webpack to bundle your code then what you are trying to achieve will not work: Since Webpack is running in build-time, it can't figure out which modules to bundle when the name is a dynamic variable. Packaging happens once before runtime so those variables don't have values yet.
If you have a small number of images, you can do something like this:
let image1 = require('../../assets/xxx.jpg');
let image2 = require('../../assets/xxx.jpg');
...
image={image1}
...
But of course, if you have a long list of images this way will not be optimal
Further details can be found in the discussion of this issue

Related

Error relating to props and renderItem in react native flatlist

im making a simple hello world type project in react native to familiarize myself with everything before I move onto my first project.
The goal of this one was to display an array with a flatlist, but the kicker was I was going to slowly modularize it (ie. array is stored somewhere else, flatlist uses renderitem, renderitem is a seperate component, ect.)
import React, {useState} from 'react';
import {StyleSheet, Text, View, FlatList} from 'react-native';
function App () {
const [orders, setOrders] = useState([
{ customer: 'Clair' , age: 45, item: 'Corn', key: 0},
{ customer: 'Alex' , age: 39, item: 'Tomato', key: 1},
]);
const renderer = ({order}) =>
{
const customerName = order.customer;
const itemName = order.item;
return(
<Text>I am {customerName} and I would like a {itemName} please</Text>
);
};
return(
<View style={styles.container}>
<FlatList
data={orders}
renderItem={renderer}
keyExtractor={(order) => order.key}
/>
<Text> Hello World!!!! </Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
},
orderContainer:
{
borderColor: 'black',
borderWidth: 5,
padding: 20,
alignItems: 'center',
},
listText:
{
justifyContent: 'center',
alignItems: 'center',
}
});
export default App;
the first dummy mistake ive learned is that you shouldnt declare a "component" (in this case the renderer) inside the main return statement for the app
now I just need to wrap my head around the prop handling. The error specifically has to do with the renderItem line. apparently the prop, which from my understanding should be one of the orders from the useState array, doesnt exist
any help is appreciated :)
The object renderer get is as follows.
item: Object
index : 0
separators: Object
Thus to get the customer details you need to update your function to follow.
const renderer = ({item}) =>
{
const customerName = item.customer;
const itemName = item.item;
return(
<Text>I am {customerName} and I would like a {itemName} please</Text>
);
};

Replace Text Component with TextInput Component - React Native

I want to display the fill in the blanks type component in the application.
For Example: Hi, my name is _______. Some more fill in _______ blanks.
I've a response something like this: {text: Hi, my name is insert_input. Some more fill in insert_input blanks.}
<Text>{item.text}<TextInput/></Text>
I'm little bit of confused, how we can achieve that.
This should be sufficient. Here is the working code
split the sentence based on the string and add them back to back by keeping text input in between them. Don't add it for the last item.
import * as React from 'react';
import { Text, View, StyleSheet, TextInput } from 'react-native';
import Constants from 'expo-constants';
// You can import from local files
import AssetExample from './components/AssetExample';
// or any pure javascript modules available in npm
import { Card } from 'react-native-paper';
export default function App() {
const string =
"Hi, my name is insert_input. Some more fill in insert_input blanks.";
const splitString = string.split("insert_input");
const modifiedString = () => {
var newStr = "";
splitString.map((subStr, i) => {
newStr = <Text>
<Text>{newStr}</Text>
<Text>{subStr}</Text>
{splitString.length - 1 === i ? null : <TextInput placeholder="________________________"/>}
</Text>;
});
console.log(newStr);
return newStr;
};
return (
<View style={styles.container}>
<Text style={styles.paragraph}>
{modifiedString()}
</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
paragraph: {
margin: 24,
fontSize: 18,
fontWeight: 'bold',
textAlign: 'center',
},
});

Using dropdown component in react-navigation throws error when handler is called

I'm trying to use react-navigation with react-native-dropdown in order to have a dropdown in the header to change what is rendered in the view below.
Whenever I try to execute the handler, react-native-dropdown I get the error "undefined is not a function (near'...this.props.data.map')when react-native-dropdown attempts to render again and maps the dropdown using (this.props.data.map((rows, index) => etc.
Below is the code I'm using:
import React, {Component} from "react"
import {Dimensions, ScrollView, StyleSheet, width, View, TouchableOpacity} from "react-native"
import {Images} from "../../theme"
import DropdownMenu from 'react-native-dropdown-menu';
import Pdf from 'react-native-pdf';
import { Header } from "react-native/Libraries/NewAppScreen";
import { connect } from 'react-redux';
import { NavigationActions } from 'react-navigation';
import { drop } from "lodash";
class InformationScreen extends Component {
constructor(props) {
super(props)
var informationScreens = props.global.appConfig.information_screens
var informationScreenTitles = Object.keys(informationScreens)
state = {
informationScreens: informationScreens,
selectedDropdown: informationScreens[0]
}
}
static navigationOptions = ({ navigation }) => {
return {
headerTitle: <DropdownMenu
style={{flex: 1}}
bgColor={'clear'}
tintColor={'#666666'}
activityTintColor={'rgba(2, 122, 255, 1.0)'}
// arrowImg={}
// checkImage={}
// optionTextStyle={{color: '#333333'}}
// titleStyle={{color: '#333333'}}
// maxHeight={300}
handler={(selection, row) => navigation.setParams({'headerTitle': navigation.state.params[selection][row]})}
data={navigation.state.params}
/>
};
};
render() {
return (
<View style={styles.container}>
<Pdf
source={{ uri: "https://someurl.com/dl/sites/default/files/page/2020%20BYU%20Football%20Almanac_3.pdf", cache: true}}
onLoadComplete={(numberOfPages,filePath)=>{
console.log(`number of pages: ${numberOfPages}`);
}}
style={styles.pdf}
/>
</View>
)
}
}
const styles = StyleSheet.create({
image: {
flex: 1,
},
container: {
flex: 1,
justifyContent: 'flex-start',
alignItems: 'center',
marginTop: 25,
},
pdf: {
flex:1,
width:Dimensions.get('window').width,
height:Dimensions.get('window').height,
}
})
function mapStateToProps(state) {
const global = state.get('global');
return { global };
}
export default connect(mapStateToProps)(InformationScreen);
Ray you were absolutely correct, I figured it out about 5 minutes before you posted.
navigation.state.params was valid and the dropdown would populate, however when I would try to setParams, it would change the format of params to a JSON object instead of an array.
This was remedied by putting my array one JSON object deeper so the object still contained the array after setParams was called. I then called that object in the data.
data={navigation.state.params.informationScreens}
Thank you so much for your help, I'll start using StackOverflow more often.
I suspect this.props.data is either not provided or not an array, so this.props.data.map is undefined, and attempting to invoke it will get you an 'undefined is not a function' error. Is navigation.state.params an array?
If you pass data={navigation.state.params || []} does the issue go away?

Pass the local image path as a prop between two functional components

I'm working on a project in react-native, where I have troubles of understanding how props works between functional components. My requirement is to create a re-usable button component where I can just pass the image location in the resource file inside my project, so it'll create the button for me. For some reason if i give the required location manually, it will work and create the button for me, but if i\I pass the location as a prop from where I create it wont work for some reason. My code as bellow.
Button component
import React, { Component } from 'react';
import {
View,
StyleSheet,
Image,
TouchableOpacity
} from 'react-native';
const ButtonWithImage = (props) => {
const {buttonStyle} = styles;
const clickEvent = () => {}
return (
<TouchableOpacity onPress= {clickEvent}style={buttonStyle}>
<Image
source={props.imagePath}
style={styles.ImageIconStyle}
/>
</TouchableOpacity>
);
};
const styles = {
buttonStyle: {
//alignSelf:'stretch',
height: 50,
width:50,
paddingTop:0,
flexDirection: 'row'
}
};
export default ButtonWithImage;
Place where I create the button and pass the prop
import React, { Component } from 'react';
import {
View,
StyleSheet,
Dimensions,
} from 'react-native';
import FooterIcons from './ButtonWithImage'
const Footer = () => {
return (
<View style={styles.footerStyle}>
<FooterIcons imagePath = {'./images/homeButton/homeBtn.png'} />
</View>
);
};
const styles = StyleSheet.create({
footerStyle: {
height: 60,
width: 100,
// justifyContent:'flex-start'
},
});
export default Footer;
This is not possible since you want to require an image with a local path
<Image source={require(props.path)} /> and this does not work because require can only take string literal as an argument.
This means that you will have to do:
<FooterIcons imagePath = {require('./images/homeButton/homeBtn.png')}
/>
To make it work.
And don't forget to give your image a width and height.
OR
You can do it in a way which works good for apps that does not have big amounts of images, because we will preload them:
1- Make an assets javascript file assets.js , this file should require all your local images, something like this:
const assetsObject = {
homeIcon: require('./images/homeButton/homeBtn.png')
boatIcon: require('./images/homeButton/boat.png')
....
...
}
module.exports = assetsObject
2- Now you need to require this file in your ButtonWithImage.js file
const assets = require('./assets.js')
const ButtonWithImage = (props) => {
const {buttonStyle} = styles;
const clickEvent = () => {}
return (
<TouchableOpacity onPress= {clickEvent}style={buttonStyle}>
<Image
source={assets[props.imagePath]}
style={styles.ImageIconStyle}
/>
</TouchableOpacity>
);
};
3- The props you send to ButtonWithImage should be on of the keys of the assetsObject we created 'homeIcon' or 'boatIcon' ..etc
const Footer = () => {
return (
<View style={styles.footerStyle}>
<FooterIcons imagePath = {'homeIcon'} />
</View>
);
};
4- Don't forget to give your image a width and height
Thats it, and i suggest not calling the prop imagePath anymore, maybe just image.
You can simply pass the value as you pass other pros.
import picture from 'linkOfYourImage.png';
function Func() {
<YourComponent imgLink={picture }/>
}

Merge / Combine two or more different StyleSheet components in React Native?

I'm separating my styles in the following way:
styles /
|-- base.js
|-- base.ios.js
|-- base.android.js
Each of them exports a StyleSheet component created as in this example:
import { StyleSheet } from 'react-native';
export default StyleSheet.create({
statusBar: {
height: 20
});
How can I merge them so I only have one base style object? I'm looking for something like:
const baseStyles = mergeStyles(baseStyle, platformStyle);
you are very close:
const baseStyles = [baseStyle, platformStyle];
basically any component can cascade styles like this:
<View style={[styles.style1,styles.style2]}></View>
You can also use StyleSheet.flatten method. See documentation here.
var styles = StyleSheet.create({
listItem: {
flex: 1,
fontSize: 16,
color: 'white',
},
selectedListItem: {
color: 'green',
},
});
StyleSheet.flatten([styles.listItem, styles.selectedListItem]);
// returns { flex: 1, fontSize: 16, color: 'green' }
UPDATE:
StyleSheet.flatten internally uses StyleSheetRegistry.getStyleByID(style) to resolve style objects represented by IDs. IDs enable optimizations through the bridge and memory in general. Referring to style objects directly will deprive you of these optimizations.
So flatten method is better than style={ [ styles.listItem, styles.selectedListItem ] }
You can combine style sheets using the spread operator '...', be warned that any variables of the same name will be overwritten by the last instance.
Heres a small demo app to demonstrate:
'use strict';
import React, { Component } from 'react';
import {
Alert,
Button,
StyleSheet,
Text,
AppRegistry,
View,
} from 'react-native';
class listTest extends Component {
render() {
return (
<View style={styles3.myViewBox}>
<Text style = {styles3.myTextBox1}>
TEST
</Text>
</View>
);
}
}
const styles = StyleSheet.create({
myTextBox1: {
backgroundColor:'red',
},
myViewBox: {
backgroundColor:'blue',
margin:15,
padding:15,
}
});
const styles2 = StyleSheet.create({
myTextBox2: {
backgroundColor:'yellow',
},
myViewBox: {
backgroundColor:'green',
margin:15,
padding:15,
},
});
const styles3 = {...styles,...styles2};
AppRegistry.registerComponent('listTest', () => listTest);
EDIT:
If you're running ES5 you can just use:
const styles3 = Object.assign(styles,styles2);
Now ReactNative lets you combine two styles using a more functional approach. Use the function StyleSheet.compose so you can do the following as per documentation:
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
const App = () => (
<View style={container}>
<Text style={text}>React Native</Text>
</View>
);
const page = StyleSheet.create({
container: {
flex: 1,
padding: 24,
backgroundColor: '#fff',
},
text: {
fontSize: 30,
color: '#000'
},
});
const lists = StyleSheet.create({
listContainer: {
flex: 1,
backgroundColor: '#61dafb',
},
listItem: {
fontStyle: 'italic',
fontWeight: 'bold'
},
});
const container = StyleSheet.compose(page.container, lists.listContainer); // if you wanna compose more than one style just use a map.reduce function and compose two at the time until you have consumed you list of styles
const text = StyleSheet.compose(page.text, lists.listItem);
export default App;

Resources