Using variables inside react native stylesheets wont recognize the variable - javascript

I import a color as props.color into my functional component and set it as the state 'tagColor'. When I use tagColor as a value in my stylesheet to set the background color i receive the error 'variable tagColor not found'
How can I use variables inside my stylesheet?
const Tag = (props) => {
const [tagColor, setColor] = useState(props.color)
return (
<View style={styles.container}>
<TouchableOpacity style={styles.tag} title='tag'>
</TouchableOpacity>
</View>
);
}
const styles = StyleSheet.create({
container: {
alignItems: "center",
justifyContent: "center",
height: 25,
display: 'flex'
},
tag: {
backgroundColor: tagColor,
}
});
export default Tag;

Well, of course it doesn't recognize tagColor, it's in a different scope. Notice tagColor is in the scope of your function component, while the StyleSheet is a different scope.
One way to solve this is to directly pass the backgroundColor to the style prop, like that:
<TouchableOpacity style={{backgroundColor: tagColor}} title="tag">
Another way is to define the tag class in your StyleSheet as a function that receives the color and returns the style, like:
const styles = StyleSheet.create({
container: {
...
},
tag: tagColor => ({
backgroundColor: tagColor,
})
});
And then use it like that:
<TouchableOpacity style={styles.tag(tagColor)} title="tag">
I'd go with the first way if you don't have any more styles other than backgroundColor. If you need more styles, go with the 2nd method.

Related

React Native: How to create elements and return them in a function

I am new to React Native
and I want to create elements and return them with a button onPress function, I donĀ“t want to hide and show like a Modal, else create them.
import React from "react"
import { Button, StyleSheet, Text, View } from 'react-native';
function createElement() {
return(
<View style={styles.elementStyle}>
<Text style={styles.txt}>ELement</Text>
</View>
)
}
const App = () => {
return (
<View style={{ flex: 1,backgroundColor: '#fff', alignItems: 'center',justifyContent: 'center',}}>
<Button title="create element" onPress={() => createElement()}/>
</View>
);
}
export default App;
const styles = StyleSheet.create({
container: {flex: 1, backgroundColor: '#fff', alignItems: 'center', justifyContent: 'center',
},
elementStyle: { backgroundColor:'grey', width:'95%', height: 90, margin: 10, justifyContent: "center", borderRadius: 10, fontWeight: "bold" },
txt: {textAlign:'center',fontSize:28,color:'#fff',fontWeight: "bold"}});
I tried with functions that return components, but nothing works
Do you want to have multiple elements or just a single modal?
For multiple elements, do the below. For a single element, it's easiest to just use show / hide logic.
The best way to do this is have an array in state like so:
const [elementArray, setElementArray] = useState();
Your createElement method instead should become two parts, adding elements to the array with the content you want, which you can then render in the main return function with a map method.
const addElement = () => {
// Just using text here. If you want a more complex element, you will have to add things to the object.
const newElementText = 'Element';
const newElementArray = elementArray.slice();
newElementArray.push(newElementText);
setElementArray([...newElementArray]);
}
Then in your return function in the component:
{elementArray.map((element) => {
return (
<View style={styles.elementStyle}>
<Text style={styles.txt}>element</Text>
</View>
);
}
)}
Make sure you add a useEffect hook so the component rerenders when you add a new element:
useEffect(()=> {}, [elementArray])
You can't navigate to a component like that. If you are making it so your component appears on the click of a button I suggest building a Stack by importing react-native/navigation. Then, building your structure as shown. My explanation might not have been the best because your initial code was unstructured. This should give you an even better answer. docs
const navigation = useNavigation();
function createElement() {
return(
<View style={styles.elementStyle}>
<Text style={styles.txt}>Element</Text>
</View>
)
}
function Home() {
return (
<View style={{ flex: 1,backgroundColor: '#fff', alignItems: 'center',justifyContent: 'center',}}>
<Button title="create element" onPress={() => navigation.navigate("Element")}/>
</View>
);
}
const App = () => {
<Stack.Navigator screenOptions={{ headerShown: false }}>
<Stack.Screen name="Home" component={Home} />
<Stack.Screen name="Element" component={CreateElement} />
</Stack.Navigator>
}

Is there an alternative for position: 'sticky in react-native?

I'm trying to make an element stay at the top of the screen at all times, vene when scrolling. I found out that position: 'sticky' does this for regular html. I was wondering if there was something I could add to my css to make the element stick to one place while scrolling using react-native.
On ScrollView there is a prop called stickyHeaderIndices. Pass the index of the component that you want to be sticky in this prop. For example, if you want to sticky the header to be sticky from the below code, you have to just pass 1 in stickyHeaderIndices props like this :
import React from 'react';
import { View, ScrollView, StyleSheet, Text } from 'react-native';
const App = () => {
return (
<ScrollView stickyHeaderIndices={[1]}>
<View>
<Text style={styles.overline}>
Overline
</Text>
</View>
<View>
<Text style={styles.header}>
Header
</Text>
</View>
{/* Your others component goes here */}
</ScrollView>
)
}
const styles = StyleSheet.create({
overline: {
fontSize: 12,
fontWeight: '100'
},
header: {
fontSize: 24,
fontWeight: 'bold'
},
spacer: {
height: 10,
}
});
export default App;
This prop accepts the array, so you can also set multiple sticky components by passing the index of those all components.

Problem with custom header in react navigation

I'm trying to make a custom header with react navigation but i can't get proper resoult. I want red view to cover all of the blue one but I can't figure it out.
Here is a picture
My HomeStack.js:
const screens = {
Home: {
screen: HomeScreen,
}
}
const options = {
defaultNavigationOptions: {
headerTitle: () => <Header/>,
headerStyle: {
backgroundColor: 'lightblue',
height: 60,
}
}
}
const HomeStack = createStackNavigator(screens, options)
const Navigator = createAppContainer(HomeStack)
export default Navigator
My Header.js:
class Header extends Component {
render() {
return(
<View style={style.conteiner}>
<Text style={style.text}>Text</Text>
</View>
)
}
}
const style = StyleSheet.create({
conteiner: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'red',
},
text: {
color: '#fff',
fontSize: 20,
}
})
export default Header
If I understand you correctly, then you want to define a custom component for the entire header. You are using your custom header component to set headerTitle:
const options = {
defaultNavigationOptions: {
headerTitle: () => <Header/>, <-- here
headerStyle: {
backgroundColor: 'lightblue',
height: 60,
}
This is what the documentation says about headerTitle:
Sometimes you need more control than just changing the text and styles
of your title -- for example, you may want to render an image in place
of the title, or make the title into a button. In these cases you can
completely override the component used for the title and provide your
own.
This option is only intended for replacing the title component, not the entire header. Unless you have some very specific requirements, you can likely achieve what you are looking for using a combination of headerStyle, headerTintColor and headerTitle.
You need to pass your custom header component to the header props for the stack navigator.
headerTitle is for the title inside the header component.
https://reactnavigation.org/docs/4.x/stack-navigator#header
https://reactnavigation.org/docs/4.x/stack-navigator#headertitle
https://reactnavigation.org/docs/4.x/stack-navigator

React Native - Animate width shrink

In the header of my React Native app, I have a conditional icon and a Searchbar.
static navigationOptions = ({ navigation }) => {
const { params = {} } = navigation.state;
return {
headerTitle: (
<View
style={{
flex: 1,
backgroundColor: Platform.OS === 'ios' ? '#e54b4d' : '',
alignItems: 'center',
flexDirection: 'row',
paddingHorizontal: 10,
height: StatusBar.currentHeight,
}}>
{params.isIconTriggered && <Icon name="chevron-left" size={28} />}
<SearchBar
round
platform={'default'}
placeholder="Search"
containerStyle={{
flex: 1,
backgroundColor: 'transparent',
}}
/>
</View>
),
headerStyle: {
backgroundColor: '#e54b4d',
},
};
};
Normally the Searchbar will take the full width of the header which is what I want. If the condition isIconTriggered is true, an icon will appear in front of the Searchbar and the width of the SearchBar will shrink enough so that the icon is visible next to it.
However, there is no transition or animation when this happens and it does not feel nor look nice. I would like to add an animation to the Searchbar so the width shrinks gradually and smoothly when the condition is triggered and the icon appears.
Is that possible to achieve and how can I achieve this?
Try to learn Animated API of react native.
Here is how i done it with button trigger.
import React, {Component} from 'react';
import {StyleSheet, View, TextInput , Button, SafeAreaView, Animated} from 'react-native';
import FA from 'react-native-vector-icons/FontAwesome5'
const AnimatedIcon = Animated.createAnimatedComponent(FA)
// make your icon animatable using createAnimatedComponent method
export default class Application extends Component {
animVal = new Animated.Value(0);
// initialize animated value to use for animation, whereas initial value is zero
interpolateIcon = this.animVal.interpolate({inputRange:[0,1], outputRange:[0,1]})
interpolateBar = this.animVal.interpolate({inputRange:[0,1],outputRange:['100%','90%']})
// initialize interpolation to control the output value that will be passed on styles
// since we will animate both search bar and icon. we need to initialize both
// on icon we will animate the scale whereas outputRange starts at 0 end in 1
// on search bar we will animate width. whereas outputRange starts at 100% end in 90%
animatedTransition = Animated.spring(this.animVal,{toValue:1})
// we use spring to make the animation bouncy . and it will animate to Value 1
clickAnimate = () => {
this.animatedTransition.start()
}
// button trigger for animation
//Components that will use on Animation must be Animated eg. Animted.View
render() {
return (
<SafeAreaView>
<View style={styles.container}>
<View style={styles.search}>
{/* our icon */}
<Animated.View style={{width: this.interpolateBar}}>
<TextInput placeholder='search here' style={styles.input}/>
</Animated.View>
<AnimatedIcon name='search' size={28} style={{paddingLeft: 10,paddingRight:10, transform:[{scale: this.interpolateIcon}]}}/>
</View>
<Button title='animate icon' onPress={this.clickAnimate}/>
</View>
</SafeAreaView>
);
}
}
const styles = StyleSheet.create({
container: {
backgroundColor:'#F79D42',
// flex: 1,
height:'100%',
paddingTop:20,
flexDirection: 'column',
// justifyContent: 'center',
alignItems:'center'
},
input:{
width: '100%',
height:40,
backgroundColor:'gray',
textAlign:'center'
},
search:{
flexDirection:'row-reverse',
width:'90%',
height:40,
alignItems:'center'
}
});
Solution using react-native-elements SearchBar component.
Wrapped the SearchBar Component inside Animated.View.
to explicitly animate the search bar
Like This:
<Animated.View style={{width: this.interpolateBar}}>
<SearchBar
placeholder="Type Here..."
containerStyle={{width: '100%'}}
/>
</Animated.View>
You can achieve this using Animated API of React Native.
You can check this tutorial for an overview of changing the size of elements with animation.
React-Native-Animatable is super cool!
Try this one out:
Create A custom animation object
import * as Animatable from 'react-native-animatable';
Animatable.initializeRegistryWithDefinitions({
const myAnimation = {
from: {
width: 200
},
to: {
width: 100
}
}
})
Use is as Animation value within a view or as a reference within a function call.
Within a view:
<Animatable.View useNativeDriver animation={myAnimation}/>
As a reference variable:
<Animatable.View useNativeDriver ref={ref=>(this.testAnimation = ref)}/>
Method:
testMethod = () => {
this.testAnimation.myAnimation();
}

Passing props into external stylesheet in React Native?

I'm new to React and React Native. At the moment for each component I'm breaking the code into 2 separate files:
index.js for all the React code, and;
styles.js for the StyleSheet
Is there a way to pass props into the external StyleSheet?
Example:
index.js:
render() {
const iconColor = this.props.color || '#000';
const iconSize = this.props.size || 25;
return (
<Icon style={styles.icon} />
);
}
Example styles.js:
const styles = StyleSheet.create({
icon : {
color: iconColor,
fontSize: iconSize
}
});
The code above does not work, but it's more there just to get the point across of what I'm trying to do. Any help is much appreciated!
I rather to have my styles in a separate file styles.js.
Inside styles.js:
export const styles = (props) => StyleSheet.create({
icon : {
color: props.iconColor,
fontSize: props.iconSize
}
}
Inside your main class you can pass the value
return (
<Icon style={styles(this.props).icon} />
);
Alternatively you can those value directly
so it would be
export const styles = (iconColor,iconSize) => StyleSheet.create({
icon : {
color: iconColor,
fontSize: iconSize
}
}
and inside your main class
return (
<Icon style={styles(this.props,iconColor,
this.props.iconSize).icon} />
);
i'm sending noFooter boolean prop in a style sheet
<View style={styles.mainFooterCont(noFooter)}>
<Text> Testing </Text>
</View>
and receiving it like
mainFooterCont: noFooter => ({
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'flex-end',
paddingBottom: noFooter ? 0 : 20,
paddingTop: Metrics.ratio(noFooter ? 0 : 5),
}),
Create a class that takes iconColor and iconSize as arguments and returns a StyleSheet object
// styles.js
export default class StyleSheetFactory {
static getSheet(iconSize, iconColor) {
return StyleSheet.create({
icon : {
color: iconColor,
fontSize: iconSize
}
})
}
}
// index.js
render() {
let myStyleSheet = StyleSheetFactory.getSheet(64, 'red')
}
Just wrap stylesheet in a function where you can optionally pass props.
Instead of:
const styles = StyleSheet.create({
Title: { color: 'white' }
});
You do:
const styles = (props?: any) => StyleSheet.create({
Title: { color: 'white' }
});
And now when you add them to your components, instead of
style={styles.Title}
You do:
style={styles(propsObjectHere).Title}
and since this is optional and you have no props to pass, just do:
style={styles().Title}
P.S. ignore the type if you, for some reason, are not using TypeScript :P
If you do not prefer to create a class then simply just create a function followed by a key and return an object from the function, you just have to pass the parameter by which you want to evaluate the condition. here is the exmple
export const Style = StyleSheet.create({
locatorTextInputContainer: locatorType => ({
flexDirection: 'row',
backgroundColor: locatorType == 'None' || locatorType == '' ? GColors.separatorColor : GColors.white,
marginBottom: 10,
paddingBottom: 5,
marginStart: 10,
marginEnd: 10,
})
})
and you can use it as follows
<View style={Style.locatorTextInputContainer(locatorType)}>
<TextInput
value={sourceLocator}
onChangeText={(text) => {
dispatch(setSourceLocator(text))
}}/>
</View>
Solution:
render() {
const iconColor = this.props.color || '#000';
const iconSize = this.props.size || 25;
return (
<Icon style={{...styles.icon, color: iconColor, fontSize: iconSize }} />
Example styles.js:
const styles = StyleSheet.create({
icon : {
color: iconColor,
fontSize: iconSize
}})
Here is a simpler solution.
Component
<View
style={{
...styles?.tabItem_bottomView,
backgroundColor: selected ? Color?.blue : Color?.white,
}}
/>
You can just use the stylesheet as before. Nothing to edit there.

Categories

Resources