Styling reusable components in React-Native - javascript

I made a reusable component Button.js and I'm importing it on two different screens. The button looks the same, but on the first screen I need it to be position: 'absolute' and on the second one position: 'relative' (the default).
How do I add the position to be absolute on the first screen? I tried to add the styling on FirstPage.js but it does not work. How do I overwrite the style that is defined in Button.js?
Button.js:
import React from 'react';
import { Text, TouchableOpacity } from 'react-native';
const Button = ({ position, onPress, children }) => {
const { buttonStyle, textStyle } = styles;
return (
<TouchableOpacity onPress={onPress} style={buttonStyle, {'position': position}}>
<Text style={textStyle}>{children}</Text>
</TouchableOpacity>
);
};
Button.defaultProps = {
position: 'relative',
}
const styles = {
textStyle: {
alignSelf: 'center',
color: '#F44336',
fontSize: 16,
},
buttonStyle: {
zIndex: 2,
width: 100,
backgroundColor: '#FFF',
}
};
export { Button };

You can pass props, something like this (Button.js) (edited according to posted code):
import React from 'react';
import { Text, TouchableOpacity } from 'react-native';
const Button = ({ position, onPress, children }) => {
const { buttonStyle, textStyle } = styles;
const style = {...buttonStyle, position };
return (
<TouchableOpacity onPress={onPress} style={style}>
<Text style={textStyle}>{children}</Text>
</TouchableOpacity>
);
};
Button.defaultProps = {
position: 'relative',
}
const styles = {
textStyle: {
alignSelf: 'center',
color: '#F44336',
fontSize: 16,
},
buttonStyle: {
zIndex: 2,
width: 100,
backgroundColor: '#FFF',
}
};
export { Button };
Your button of course looks different, this is just an outline of what you could do (basically just using props).

This is REUSABLE Button as touchableOpacity.
export const NormalThemeButton = (props) => {
return (
<TouchableOpacity
style={[{ ...props.style, ...styles.normalThemeBtn }]}
style={[{ alignItems: props.align }, styles.anchor]}
onPress={props.onPress} >
<CustomText text={props.text} l bold style={{
textAlign: 'center',
color: theme['blackColor']
}} />
{props.children}
</TouchableOpacity >
);
};
This is where this RESUABLE Button is been used.
style={{ ...styles.openArrivedButton }}
text={Lng.instance.translate('useWaze')}
onPress={() => { Waze() }}/>
Hope You find it helpful.

Related

How I can find variable: Name in react-native?

I'm trying to pass the 'data' to other archive (CampaignList.js > Campaign.js) but I don't now why the variable is not pass to Campaign.js function. I watched some videos on YouTube and read some forums and nothing help.
CampaignList.js
import React from 'react';
import {
StyleSheet,
Dimensions,
View,
Text,
TouchableOpacity,
FlatList
} from 'react-native';
import Campaign from './Campaign';
var deviceWidth = Dimensions.get('window').width;
var deviceHeight = Dimensions.get('window').height;
const App: () => React$Node = () => {
return (
<>
<View style={styles.screen}>
<FlatList
style={styles.scroll}
contentContainerStyle={{ paddingHorizontal: 20 }}
showsVerticalScrollIndicator={false}
keyboardShouldPersistTaps="handled"
data={[
{
id: 1,
name: 'Peregrino da Alvorada'
}
]}
keyExtractor={(item) => String(item.id)}
renderItem={({ item }) => <Campaign data={item} />}
/>
<TouchableOpacity>
<View style={styles.button}>
<Text style={styles.text}>ADICIONAR CAMPANHA</Text>
</View>
</TouchableOpacity>
</View>
</>
);
};
const styles = StyleSheet.create({
screen: {
flex: 1,
flexDirection: 'column'
},
scroll: {
marginHorizontal: deviceWidth * 0.05,
marginVertical: deviceHeight * 0.025,
height: deviceHeight * 0.6,
borderWidth: 1,
borderColor: 'black'
},
button: {
marginHorizontal: deviceWidth * 0.05,
marginVertical: deviceHeight * 0.025,
width: deviceWidth * 0.9,
height: deviceHeight * 0.1,
backgroundColor: '#6a0301',
alignItems: 'center',
justifyContent: 'center'
},
text: {
color: 'white',
fontWeight: 'bold'
}
});
export default App;
My idea is pass the data to here to create a query with RealmDB but the MetroServer returt an error "Can't find variable: Name" in the Campaign.js:
Campaign.js
import React from 'react';
import { View, StyleSheet } from 'react-native';
export default function Campaign({ data }) {
return (
<View>
<Name>{data.name}</Name>
<Id>{data.id}</Id>
</View>
);
}
const styles = StyleSheet.create({
name: {
fontSize: 50,
color: 'black'
},
id: {
fontSize: 10,
color: 'grey'
}
});
You should be using Text for displaying your text components, or else you can create two different components named Name and Id ( not at all required in this use case) to display the items
import React from 'react';
import {View, StyleSheet,Text} from 'react-native';
export default function Campaign({ data }) {
return(
<View>
<Text>{data.name}</Text>
<Text>{data.id}</Text>
</View>
);
}
const styles = StyleSheet.create({
name: { fontSize: 50, color: 'black' },
id: { fontSize: 10, color: 'grey' },
});
In the original question, you were trying to use undefined components Name and Id.

React Native, Cover (Overlay) the Requested Page with Loading Indicator and setTimeout() to Hide it

I have this code and it works fine to shows the overlay the requested page for 5sec and Hide to shows the requested page's contents, But when the Loader indicator disappeared its (red) background still there, how to hide the background too?
It has two part firsts one for creating Loading Indicator to be hidden after 5 sec.
Working example on Expo.io:
Live Demo -
This is requested page's code: (Please, notice has to be called from /screens)
import * as React from 'react';
import { Text, View, StyleSheet } from 'react-native';
import Loader from './screens/Loader';
export default function App() {
return (
<View style={styles.container}>
<Loader /> //<--- I put the Loader here
<Text style={styles.paragraph}>
This is the requested page should be be covered by indicator background (Red color) <br/ >
The Loader disappear after 5 sec.<br />
But, its background still there!!
</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',
},
});
And the Loader.js code is :
import React, { Component } from 'react';
import { ActivityIndicator, View, Text, TouchableOpacity, StyleSheet } from 'react-native';
class Loader extends Component {
state = { animating: true }
closeActivityIndicator = () => setTimeout(() => this.setState({
animating: false }), 5000)
componentDidMount = () => this.closeActivityIndicator()
render() {
const animating = this.state.animating
return (
<View style = {styles.container}>
<ActivityIndicator
animating = {animating}
color = '#bc2b78'
size = "large"
style = {styles.activityIndicator}/>
</View>
)
}
}
export default Loader
const styles = StyleSheet.create ({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
marginTop: 0,
position: 'absolute',
height: "100%",
width: "100%",
backgroundColor: 'red',
opacity: 0.7
},
activityIndicator: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
}
})
The problem is you are always rendering the Loader and its always visible, so the easiest way to handle this would be to return null and hide the loader when its not necessary like below.
render() {
const animating = this.state.animating;
if(!this.state.animating)
return null;
return (
<View style = {styles.container}>
<ActivityIndicator
animating = {animating}
color = '#bc2b78'
size = "large"
style = {styles.activityIndicator}/>
</View>
)
}

Adding a DOM element with the press of a button in react native

I want to be able to add a <Text> element with the press of a button in react native
Is this possible to make ? and how can i do it ?
my code :
import React, { Component } from 'react'
import { StyleSheet, Text, View, Button } from 'react-native'
export default class App extends Component {
onPress = () => {
//some script to add text
}
render() {
return (
<View style = { styles.container }>
<View style = { styles.ButtonContainer }>
//i want to add text here
<Button
onPress={this.onPress}
title="Add item"
color="#FB3640"
accessibilityLabel="Add item"
containerViewStyle={{width: '100%', marginLeft: 0}}
/>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'column',
justifyContent: 'space-between',
backgroundColor: '#fff',
alignItems: 'center',
},
text: {
marginTop: 100,
},
ButtonContainer: {
margin: 20,
width: '90%',
}
});
Thank you !
You need to define a piece of state and initialized it with false. When the user presses the button you have to switch this piece of state to true. Have a look here for more information about the local state: https://reactjs.org/docs/state-and-lifecycle.html#adding-local-state-to-a-class
Something like that should work:
import React, { Component } from 'react'
import { StyleSheet, Text, View, Button } from 'react-native'
export default class App extends Component {
state = {
displayText: false
}
onPress = () => {
this.setState({ displayText: true });
}
render() {
return (
<View style = { styles.container }>
<View style = { styles.ButtonContainer }>
{displayText && <Text>This is my text</Text>}
<Button
onPress={this.onPress}
title="Add item"
color="#FB3640"
accessibilityLabel="Add item"
containerViewStyle={{width: '100%', marginLeft: 0}}
/>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'column',
justifyContent: 'space-between',
backgroundColor: '#fff',
alignItems: 'center',
},
text: {
marginTop: 100,
},
ButtonContainer: {
margin: 20,
width: '90%',
}
});

React Native: Touchable Opacity element is clickable on iOS but not Android

I'm working on a React Native app with a typeahead component. The typeahead displays options that overlay other content on the route (see right image below). When a user clicks one of those options, an onPress listener runs a function:
This all works just fine on iOS. On Android though, the onPress event is never received. Even more strangely, when I try to click on an option lower in the list (like Boston, MA, USA), the onPress event is received by the card below the pressed option (Djerba).
Does anyone know what might cause this behavior? I'd be super grateful for any insights others can offer on this query.
Here's the code for the Explore view and the typeahead components.
Explore.js
import React from 'react'
import { connect } from 'react-redux'
import { Text, View, ScrollView, TouchableOpacity } from 'react-native'
import { gradients, sizing } from '../../style'
import { LinearGradient } from 'expo-linear-gradient'
import { MountainHero } from '../Heros'
import { CardRow } from '../Card'
import Loading from '../Loading'
import { setExploreSearch, onExploreTypeaheadClick } from '../../actions/locations'
import { Typeahead } from '../Typeahead'
const styles = {
container: {
flex: 1,
flexDirection: 'column',
},
scrollView: {
paddingBottom: sizing.margin,
},
loadingContainer: {
position: 'absolute',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
zIndex: 100,
elevation: 100,
top: 53,
width: '100%',
},
typeahead: {
margin: sizing.margin,
marginBottom: 0,
width: sizing.screen.width - (2*sizing.margin),
zIndex: 100,
elevation: 100,
}
}
const Explore = props => {
const { authenticated: a, spotlight, loading } = props;
let r = (a.recommendedLocations || []);
if (!r || !spotlight) return null;
// remove spotlight locations from the recommended locations
const ids = spotlight.map(i => i.guid);
const recommended = r.filter(i => ids.indexOf(i.guid) == -1);
return (
<LinearGradient style={styles.container} colors={gradients.teal}>
<ScrollView contentContainerStyle={styles.scrollView}>
{loading && (
<View style={styles.loadingContainer}>
<Loading />
</View>
)}
<MountainHero text='Explore' />
<Typeahead
style={styles.typeahead}
placeholder='Search Cities'
value={props.exploreSearch}
onChange={props.setExploreSearch}
vals={props.exploreTypeahead}
valKey={'place_id'}
onTypeaheadClick={props.onExploreTypeaheadClick}
/>
<CardRow
text='Explore Places'
cards={recommended}
type='location' />
<CardRow
text='In the Spotlight'
cards={spotlight}
type='location' />
</ScrollView>
</LinearGradient>
)
}
const mapStateToProps = state => ({
authenticated: state.users.authenticated,
spotlight: state.locations.spotlight,
exploreSearch: state.locations.exploreSearch,
exploreTypeahead: state.locations.exploreTypeahead,
loading: state.locations.loading,
})
const mapDispatchToProps = dispatch => ({
setExploreSearch: s => dispatch(setExploreSearch(s)),
onExploreTypeaheadClick: val => dispatch(onExploreTypeaheadClick(val)),
})
export default connect(mapStateToProps, mapDispatchToProps)(Explore)
Typeahead.js
import React from 'react'
import { Text, View, TouchableOpacity } from 'react-native'
import { sizing, GradientInput } from '../style'
const styles = {
container: {
position: 'absolute',
zIndex: 100,
elevation: 100,
height: 400,
width: '100%',
},
input: {
width: '100%',
borderRadius: 0,
},
typeaheadContainer: {
position: 'absolute',
zIndex: 100,
elevation: 100,
top: 55,
width: '100%',
},
typeaheadRow: {
padding: 10,
paddingTop: 12,
paddingBottom: 12,
borderWidth: 1,
borderColor: '#eeeeee',
backgroundColor: '#ffffff',
marginBottom: -1,
},
typeaheadRowText: {
fontSize: 15,
fontFamily: 'open-sans',
lineHeight: 20,
backgroundColor: '#ffffff',
},
}
export const Typeahead = props => {
return (
<View style={[props.container, props.style]}>
<GradientInput style={styles.input}
placeholder={props.placeholder}
value={props.value}
onChange={props.onChange} />
<TypeaheadList vals={props.vals}
valKey={props.valKey}
onTypeaheadClick={props.onTypeaheadClick} />
</View>
)
}
export const TypeaheadList = props => {
if (!props.vals) return null;
return (
<View style={styles.typeaheadContainer}>
{props.vals.map(i => {
let text = i.text;
if (text.length > 31) text = text.substring(0,31) + '...';
return (
<TouchableOpacity activeOpacity={0.5} key={i[props.valKey]}
style={styles.typeaheadRow}
onPress={() => props.onTypeaheadClick(i[props.valKey])}>
<Text numberOfLines={1} style={styles.typeaheadRowText}>{text}</Text>
</TouchableOpacity>
)
})}
</View>
)
}
export default Typeahead
Try to move Typeahead component below all CardRow components and set position:absolute for Typeahead. Probably on android - the latest view shadow all views before (I am not sure, but I think you have to try it for next discovering issue).
You should also remove position: absolute from all but one component. Working code:
Explore.js
import React from 'react'
import { connect } from 'react-redux'
import { Text, View, ScrollView, TouchableOpacity } from 'react-native'
import { gradients, sizing } from '../../style'
import { LinearGradient } from 'expo-linear-gradient'
import { MountainHero } from '../Heros'
import { CardRow } from '../Card'
import Loading from '../Loading'
import { setExploreSearch, onExploreTypeaheadClick } from '../../actions/locations'
import { Typeahead } from '../Typeahead'
const styles = {
container: {
flex: 1,
flexDirection: 'column',
},
scrollView: {
paddingBottom: sizing.margin,
},
loadingContainer: {
position: 'absolute',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
zIndex: 1,
elevation: 1,
top: 53,
width: '100%',
},
topCardRow: {
paddingTop: sizing.margin + sizing.gradientInput.height,
},
typeahead: {
margin: sizing.margin,
marginBottom: 0,
width: sizing.screen.width - (2*sizing.margin),
zIndex: 1,
elevation: 1,
position: 'absolute',
top: sizing.mountainHero.height,
left: 0,
}
}
const Explore = props => {
const { authenticated: a, spotlight, loading } = props;
let r = (a.recommendedLocations || []);
if (!r || !spotlight) return null;
// remove spotlight locations from the recommended locations
const ids = spotlight.map(i => i.guid);
const recommended = r.filter(i => ids.indexOf(i.guid) == -1);
return (
<LinearGradient style={styles.container} colors={gradients.teal}>
<ScrollView contentContainerStyle={styles.scrollView}>
{loading && (
<View style={styles.loadingContainer}>
<Loading />
</View>
)}
<MountainHero text='Explore' />
<CardRow
style={styles.topCardRow}
text='Explore Places'
cards={recommended}
type='location' />
<CardRow
text='In the Spotlight'
cards={spotlight}
type='location' />
<Typeahead
style={styles.typeahead}
placeholder='Search Cities'
value={props.exploreSearch}
onChange={props.setExploreSearch}
vals={props.exploreTypeahead}
valKey={'place_id'}
onTypeaheadClick={props.onExploreTypeaheadClick}
/>
</ScrollView>
</LinearGradient>
)
}
const mapStateToProps = state => ({
authenticated: state.users.authenticated,
spotlight: state.locations.spotlight,
exploreSearch: state.locations.exploreSearch,
exploreTypeahead: state.locations.exploreTypeahead,
loading: state.locations.loading,
})
const mapDispatchToProps = dispatch => ({
setExploreSearch: s => dispatch(setExploreSearch(s)),
onExploreTypeaheadClick: val => dispatch(onExploreTypeaheadClick(val)),
})
export default connect(mapStateToProps, mapDispatchToProps)(Explore)
Typeahead.js
import React from 'react'
import { Text, View, TouchableOpacity } from 'react-native'
import { sizing, GradientInput } from '../style'
const styles = {
container: {
zIndex: 1,
elevation: 1,
height: 400,
width: '100%',
},
input: {
width: '100%',
borderRadius: 0,
},
typeaheadContainer: {
zIndex: 1,
elevation: 1,
top: 0,
width: '100%',
},
typeaheadRow: {
padding: 10,
paddingTop: 12,
paddingBottom: 12,
borderWidth: 1,
borderColor: '#eeeeee',
backgroundColor: '#ffffff',
marginBottom: -1,
zIndex: 1,
elevation: 1,
},
typeaheadRowText: {
fontSize: 15,
fontFamily: 'open-sans',
lineHeight: 20,
backgroundColor: '#ffffff',
},
}
export const Typeahead = props => {
return (
<View style={[props.container, props.style]}>
<GradientInput style={styles.input}
placeholder={props.placeholder}
value={props.value}
onChange={props.onChange} />
<TypeaheadList vals={props.vals}
valKey={props.valKey}
onTypeaheadClick={props.onTypeaheadClick} />
</View>
)
}
export const TypeaheadList = props => {
if (!props.vals) return null;
return (
<View style={styles.typeaheadContainer}>
{props.vals.map(i => {
let text = i.text;
if (text.length > 31) text = text.substring(0,31) + '...';
return (
<TouchableOpacity activeOpacity={0.5} key={i[props.valKey]}
style={styles.typeaheadRow}
onPress={() => props.onTypeaheadClick(i[props.valKey])}>
<Text numberOfLines={1} style={styles.typeaheadRowText}>{text}</Text>
</TouchableOpacity>
)
})}
</View>
)
}
export default Typeahead

Reset to default slider value react-native

I try to create a reset button for the slider that will reset it to the initial value when pressed. My code doesn't show any error but the reset button doesn't work.
This is the Slider component:
import React, {Component} from 'react';
import {Slider,
Text,
StyleSheet,
View} from 'react-native';
import {ButtonReset} from './ResetButton.js'
export class SliderRoll extends Component {
state = {
value: 1,
};
resetSliderValue = () => this.setState({value : 1})
render() {
return (
<View style={styles.container}>
<Slider style={styles.slider}
minimumTrackTintColor={'blue'}
maximumTrackTintColor={'red'}
maximumValue = {2}
value= {this.state.value}
step={0.5}
onValueChange={(value) => this.setState({value: value})}
/>
<ButtonReset resetSliderValue = {this.state.resetSliderValue}/>
<Text style={styles.text} >
{this.state.value}
</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flexDirection:'column',
width: 150,
backgroundColor: 'pink',
marginLeft: 50,
justifyContent: 'center'
},
slider: {
width: 150,
},
text: {
fontSize: 20,
textAlign: 'center',
fontWeight: '500',
},
})
This is the Reset button:
import React, {Component} from 'react';
import {
Text,
View,
StyleSheet,
Image,
TouchableOpacity
} from 'react-native';
export class ButtonReset extends Component{
render(){
return(
<TouchableOpacity style = {styles.container}
onPress= {this.props.resetSliderValue}>
<Image
style={styles.button}
source={require('./restart.png')}
/>
</TouchableOpacity>
);
}
}
const styles = StyleSheet.create({
container: {
width: 50,
alignItems: 'center',
backgroundColor: 'pink',
marginLeft: 50,
marginTop: 10,
},
button: {
width:50,
height:50,
transform: [
{scale: 1}
]
}
})
Here is the problematic piece that stuck out right away:
<ButtonReset resetSliderValue = {this.state.resetSliderValue}/>
I believe it should be:
<ButtonReset resetSliderValue = {this.resetSliderValue}/>
Also, I believe you need to bind this so you can access this for setting state in the resetSliderValue function. I personally like to bind this once in the constructor for any functions that require it so no unnecessary rebinding during rendering:
constructor(props) {
super(props);
this.resetSliderValue = this.resetSliderValue.bind(this);
}

Categories