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

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 }/>
}

Related

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?

GetElementById equivalent in React Native

I am working on creating a module for React-Native.
The module needs to highlight or point out certain elements on the screen based on configuration stored in a DB.
In Android, our database would consist of view ids and our code would highlight those view ids.
In HTML, the element Ids would be sufficient.
How do I define unique identifiers in React native.
These would be stored in a database and then highlighted in real-time. So the module would need to access the x, y, width and height of the elements in runtime
In HTML, I would use getElementbyId() and proceed to get its attributes. What would be the equivalent in React Native.
This works -
this.userNameRef.measure((width,height,fx,fy,px,py)=>{
console.log(fx)
console.log(fy)
})
If userNameRef is received as a string parameter, how can I get this data
First of all, have in your mind, the selection within React Native is different the web.
You don't select the element directly, you must take the reference through the prop of element.
All elements in React Native has a prop called ref, you will able to have access all informations about that component, you may call the methods and get properties values.
Example using class component:
class MyComponent extends React.Component {
onEndLoadImage = () => {
console.log(this.flatlist.current); // here have all methods and properties of Image
}
render() {
return (
<Image
ref={element => this.flatlist = element}
onLoadEnd={onEndLoadImage}
...
/>
);
}
}
Example using functional components with hooks:
function MyComponent {
const imageRef = useRef(null);
const onEndLoadImage = () => {
console.log(imageRef.current); // here have all methods and properties of Image
}
return (
<Image
ref={imageRef}
onLoadEnd={onEndLoadImage}
...
/>
);
}
You can use onLayout props on the required View to get the view dimensions on the React Native side.
import React from "react";
import {Text, View, StyleSheet} from "react-native";
const GetLayout = (props) => {
const _getLayout = (evt) => {
const data = evt.nativeEvent;
alert(JSON.stringify(data));
}
return(
<View style={{flex: 1}}>
<View style={{flex: 1}} onLayout={_getLayout}>
<Text>I am the required View</Text>
</View>
</View>
)
}
export default GetLayout;
const styles = StyleSheet.create({
})

this.props.navigation.navigate() doesn't work (using WithNavigation)

I have implemented a pop-up component in a header with More Options button. This button is available only on the UserProfileScreen. When you press on it you see two buttons: 'Settings' and 'Log out'. The issue is related to Settings button.
Expected behavior:
Press on Settings button -> this.props.navigation.navigate('SettingsNavigator') is called -> SettingsScreen is shown
Actual behavior:
Press on Settings button -> console.warn('go to settings') is called and nothing more (you are not redirected to SettingsScreen).
SettingsNavigator.js
import React from 'react';
import { createStackNavigator } from 'react-navigation';
import SettingsScreen from './SettingsScreen';
import EditProfileScreen from './EditProfileScreen';
import RemoveProfileScreen from './RemoveProfileScreen';
const SettingsNavigator = createStackNavigator({
SettingsScreen: SettingsScreen,
EditProfile: EditProfileScreen,
RemoveProfile: RemoveProfileScreen
}, {
initialRouteName: 'SettingsScreen'
});
export default SettingsNavigator;
optionHeaderButton.js
import { withNavigation } from 'react-navigation';
...
class OptionsHeaderButton extends Component {
...
navigateToSettings = () => {
console.warn('go to settings')
this.props.navigation.navigate('SettingsNavigator');
}
render() {
return(
<Menu onSelect = {value => {
switch(value) {
case 'Settings':
this.navigateToSettings();
break;
case 'Logout':
this.onLogOutPress();
break;
}
}}>
<MenuTrigger>
<Icon name = 'dots-vertical' size = {24} color = {text}/>
</MenuTrigger>
<MenuOptions style = {optionsHeaderButtonStyle.menuWrapper}>
<MenuOption value = {'Settings'}>
<View style = {optionsHeaderButtonStyle.optionWrapper}>
<View style = {optionsHeaderButtonStyle.optionIcon}>
<IconSimpleLine name = 'settings' size = {12} color = {text}/>
</View>
<Text style = {[optionsHeaderButtonStyle.option, optionsHeaderButtonStyle.lastOption]}>
Settings
</Text>
</View>
</MenuOption>
<MenuOption value = {'Logout'}>
<View style = {optionsHeaderButtonStyle.optionWrapper}>
<View style = {optionsHeaderButtonStyle.optionIcon}>
<IconSimpleLine name = 'logout' size = {12} color = {text}/>
</View>
<Text style = {[optionsHeaderButtonStyle.option, optionsHeaderButtonStyle.lastOption]}>
Logout
</Text>
</View>
</MenuOption>
</MenuOptions>
</Menu>
)
}
}
export default withNavigation(OptionsHeaderButton);
OptionsHeaderButton is nested to a Header Component that is nested in UserProfile Component.
As far as I know using withNavigation() I can call navigation.navigate() from any component. So why doesn't it work?
UPDATE:
After investigation I found that this issue is related to a bug of React Navigation that was found on April of 2017. It means that you can use navigation.navigate() only inside one Stack. But I can't find any info about fix of this bug.
you need to pass screen name instead of navigator.
this.props.navigation.navigate('SettingsSecreen');
try this:
import { NavigationActions, withNavigation } from 'react-navigation';
navigateToSettings = () => {
const navigateAction = NavigationActions.navigate({ routeName: 'SettingsScreen' });
this.props.navigation.dispatch(navigateAction);
}
I had this issue but I have wired up my App.js slightly differently using stack navigation and drawer navigation (And switch navigation for auth) - mostly followed the react navigation docs:
App.js
const AuthStack = createStackNavigator({ Login: Login }, { headerMode: 'none' });
const ActivityStack = createStackNavigator({ Activity: ActivityScreen}, {headerMode: 'none'});
// setup my routing & config here
export default createAppContainer(createSwitchNavigator(
{
Auth: AuthStack,
ActivityStack: ActivityStack,
},
{
intitalRouteName: 'Auth',
},
));
In the screen I want to navigate from I wire up the constructor like this (to bind the navigation function):
FirstScreen.js
export default class FirstScreen extends React.Component {
constructor(props) {
super(props);
this.navigateToScreen = this.navigateToScreen.bind(this);
}
state = {
// state items here
}
navigateToScreen(screenName) {
this.props.navigation.navigate(screenName);
}
render() {
return (
<View style={styles.container}>
<TouchableOpacity style={styles.button} onPress=
{this.navigateToScreen("Activity")}>
<Text style={styles.loginText}>
ADD ACTIVITY
</Text>
</TouchableOpacity>
</View>
Note how I am calling the navigation, I am only passing in the route alias I gave earlier in the App.js file:
Activity: ActivityStack,
So I would write this.navigateToScreen("Activity") from the calling point passing in the alias not the screen name or stack name.
This navigates correctly to the screen and utilizes stack navigation I defined earlier in App.js.
Hope this makes sense (parent child hierarchy is achieved by nesting the various navigation features like switch, stack and drawer to achieve your desired results.
in react-navigation Version: 5.x withNavigation is not working. To fix this issue try this.
import { useNavigation } from '#react-navigation/native';
const navigation = useNavigation();
navigation.navigate("DetailsScreen")
no need to warp the component in withNavigation.
Hope this may helps.

Pass TextInput props between different screens in React Navigation

I am totally lost on how to grab two parameters from a form screen and pass them via React Navigator and display them on the previous screen.
The app section in question works like this:
1. touchablehighlight to form screen.
2. input title and description and press submit onpress
3. the onpress runs a function that dispatches the parameters to the previous page via a key.
4. then returns back to the origin page, with the props on display.
I am having multiple issues with the process:
1. if I am understanding the docs correctly, each page has a unique key and i tried to find it via this.props.navigation.state.key, however unknown to me, on refresh the id number would change.
2. that leads to problem 2 where the function will run, but it will not redirect back to the original page.
3. i have tried .navigate line after .dispatch but it would open a new copy of the original page and not display the new props that supposively were passed down.
import React from 'react';
import styles from '../styling/style.js';
import { Text, View, Image, TouchableHighlight, TextInput, Alert } from 'react-native';
import { StackNavigator, NavigationActions } from 'react-navigation';
import Forms from './formGenerator';
export default class Workout extends React.Component {
constructor(props) {
super(props);
this.state = {
programTitle: '',
programDescription: ''
}
}
render() {
const {navigate} = this.props.navigation;
return (
<Image style={styles.workoutContainer, { flex: 1}} source={require("../images/HomepageBackground.jpg")}>
<View style={styles.workoutBody}>
<Text style={styles.workoutTextBody}> {this.state.programTitle}</Text>
<Text style={styles.workoutTextBody}>{this.state.programDescription}</Text>
</View>
<View style={styles.createButton}>
<TouchableHighlight onPress={Alert.alert(this.props.navigation.state.key)} style={styles.addButtonTouch} title='test' >
<Text style={styles.addButtonText}>+</Text>
</TouchableHighlight>
</View>
</Image>
);
}
// End of the render
}
import React from 'react';
import styles from '../styling/style.js';
import { Text, View, Image, TouchableHighlight, TextInput, Button, Alert } from 'react-native';
import Workout from './workouts';
import { NavigationActions } from 'react-navigation';
export default class Forms extends React.Component {
constructor(props) {
super(props);
this.state = {
programTitle: '',
programDescription: ''
}
}
render() {
const {goBack} = this.props.navigation;
const {params} = this.props.navigation.state;
return (
<Image style={styles.workoutContainer, { flex: 1}} source={require("../images/HomepageBackground.jpg")}>
<View style={styles.workoutBody}>
<Text style={styles.formHeader}>Program Title</Text>
<TextInput
autoFocus={true}
style={styles.formBody}
onChangeText={(programTitle) => this.setState({programTitle})}
placeholder='Title'
value={this.state.programTitle} />
<Text style={styles.formHeader}>Description (Ex. 4sets x 10reps)</Text>
<TextInput
autoFocus={true}
style={styles.formBody}
placeholder='Description'
onChangeText={(programDescription) => this.setState({programDescription})}
value={this.state.programDescription} />
<TouchableHighlight onPress={this.addProgram} style={styles.buttonBody} title="Add Program" >
<Text>Add Program</Text>
</TouchableHighlight>
</View>
</Image>
);
}
addProgram = () => {
Alert.alert(this.props.navigation.state.key);
this.setState({programTitle: ''});
this.setState({programDescription: ''});
const setParamsAction = NavigationActions.setParams({
params: { programTitle: this.state.programTitle, programDescription: this.state.programDescription },
key: ,
})
this.props.navigation.dispatch(setParamsAction)
};
}
If you are trying to get parameter from "Next Page", you could have two approaches.
1, save the params in AsyncStorage (suggested)
2, using navigation setParams function with the params
const setParamsAction = NavigationActions.setParams({
params: { title: 'Hello' },
key: 'screen-123',
})
this.props.navigation.dispatch(setParamsAction)
https://reactnavigation.org/docs/navigators/navigation-actions
You are just trying to display information from the Forms class on the Workout class, correct??
From your Workout class, create a function that update's it's state.
updateWorkoutState = (programTitle,programDescription) => this.setState(programTitle,programDescription)
Pass that function through to the Forms class when you push that route.
this.props.navigation.navigate('Forms',{updateWorkoutState: this.updateWorkoutState}
Once your conditions are met on the Forms class and you want to update the Workout component, call it with this.props.navigation.state.params.updateWorkoutState(val1,val2)
Do not use AsyncStorage for this.

when i use const { myCardStyle } = this.myStyles; at the class below it shows error

I'm learning react native. in my application, for one of the components, when i use this. at const { myCardStyle } = this.myStyles; at the class below it shows error: Possible unhandled Promise Rejection ..., but when i put it without this it works.
const Card = (myProps) => {
const { myCardStyle } = myStyles;
console.log(8 + myProps.children);
return(
<View style={myCardStyle}>
{myProps.children}
</View>
);
};
const myStyles = {
myCardStyle: {
blabla
}
};
const can be accessed without the help of this keyword. Look at the example below. styles can be accessed globally by the class without using this keyword.
this keyword is used when we want to access state or prop objects that are binded to the class. Normally u can bind the finctions using bind keyword
/**
* Sample React Native App
* https://github.com/facebook/react-native
* #flow
*/
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View
} from 'react-native';
export default class VideoExample extends Component {
__click(){
//if u want to access this keyword here
//u have to bind this function
}
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>
Welcome to React Native!
</Text>
<Text onPresss={this._click.bind(this)}/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
});
AppRegistry.registerComponent('VideoExample', () => VideoExample);
Try to move const myStyle inside Card method and before
const { myCardStyle } = myStyles
const Card = (myProps) => {
const myStyles = {
myCardStyle: {
blabla
}
};
const { myCardStyle } = myStyles;
console.log(8 + myProps.children);
return(
<View style={myCardStyle}>
{myProps.children}
</View>
);
};
styles are defined outside the class VideoExample so you can't access it using the this.myStyles.
You can directly refer the variable by it's name and if you want to access it in another class you can access it by VideoExample.myStyles.

Resources