Passing Styles Based on Parent Component in React Native - javascript

I have a <Text> component that is being passed a style so..
TextFile.js:
<Text style={styles.text}>
This is a line of text and this might be a second line
</Text>
screenFile.js:
<View style={styles.viewContainer}>
<TextFile style={styles.textWithinContainer}>
</View>
textFiles/styles.js:
text: {
fontSize: 20,
color: 'black',
fontWeight: '400',
}
screenFiles/styles.js:
textWithinContainer: {
textAlign: 'center',
}
textAlign within textWithInContainer is not being applied. If I add textAlign: 'center' to styles.text gives me the style I want but it's being used in different screens and I only want it centered in the screenFile. I want the styles from styles.textWithinContainer to override the styles in styles.text. How would I go about this?

You aren't delegating the styles you pass to TextFile to the actual Text element in TextFile. What you can do is add the styles together by passing an array of style objects to apply it:
<Text style={[styles.text, props.style]}>
This is a line of text and this might be a second line
</Text>
From the React Native documentation:
You can also pass an array of styles - the last style in the array has precedence, so you can use this to inherit styles.
Thus, if you pass textAlign in textWithContainer, it'll be applied in the Text component, and it can be reused as you wish without textAlign.

In my initial TextFile, I passed style as an argument, and in the styles array, just used style as the second item in the array.
const TextFile = ({ text, style }) => (
<Text style=([styles.text, style])> {text} </Text>
);
Whenever TextFile gets used, it will apply any styles being given within that component, and/or default to the initial styles it's being given in styles.text.
Thank you #Li357!

Related

When should I use style instead of sx prop in Material-UI?

The style and sx prop in MUI components pretty much do the same thing. The sx prop offers a few shorthand syntaxes, and allows you to access the theme object. But apart from that, they seem identical. When are you supposed to use one over the other?
To really understand which one to use, we need to understand what's happening under the hood. Material UI uses emotion(, or whatever styling engine you chose manually), in order to style its components. On the surface, the following two might seem to be doing the same thing:
<Box sx={{ height:'50px', width:'25px' }}/>
<div style={{ height:'50px', width:'25px' }}/>
Both render divs with the required height and width, to the DOM. But for the div, the styles are applied as inline styles, whereas the Box applies the styles in the form of a class, to the div element. The class definition itself, is stored in the head tag, which you can inspect, to see the following
This is all well and fine, as long as we're declaring the styles only once. But stuff really goes crazy when you add dynamic styling. Perhaps there is a state variable controlling the height of your div.
function CustomComponent(){
const [computedHeight,setComputedHeight]=useState();
useEffect(()=>{
window.addEventListener('resize',()=>setComputedHeight(window.innerWidth/2))
},[]);
return (
<Box sx={{ height:computedHeight, width:'25px'}}/>
<div style={{ height:computedHeight, width:'25px'}}/>
)
This is a common scenario, where some external variable(the width of the browser window for eg.) determines some property of your component. What happens is every time this state changes into a new value, MUI creates a new class, sets it as the class for the Box, and adds the definition to the <head> tag of your website, as a brand new <style> tag. So in very little time, the head tag fills up with hundreds of style tags, which is clearly undesirable.
However, in the case of your div tag, the styles are located inline. So no matter if the value changes once, or a hundred times, there is only one definition of the style, and it exists on the element itself. No need to worry about it.
EDIT 1:
MUI creates a new style tag only for a style that hasn't been used before. To illustrate, if your sx prop dynamically changes the color between 'red' and 'blue' like this,
sx={{
color: dynamicValue ? 'red' : 'blue',
}}
MUI will only create two tags(for the two possible values of color), no matter how many times you change the value of dynamicValue. MUI will just use the old style tags.
Note on Pseudo selectors:
Another thing to note is that inline styles cannot make use of pseudo elements(like ::after, or ::before), or pseudo classes(like :hover, :focus etc.), as inline styles directly affect the current element. You would have to employ a workaround like css variables in order to change the styles on pseudo elements/classes.
TLDR; Put your dynamic styles(the ones that change based on some variable) in the style prop, and put all the static styles in the sx prop
style vs sx in MUI 5
Sandbox Link
sx prop works only on MUI components like Grid, Box and so on, whereas style prop works on both MUI components and HTML like elements (JSX) such as span, article,h1 and so on.
sx prop is very helpful when compared to style prop in some cases as explained below. There might be many differences between them but these are the 3 key differences I have noticed and you might encounter them often.
Defining Media queries
Nesting styles
Making use of theme parameter (to get palette) inside sx object which we can't do in style like this - color: (theme) => theme.palette.primary.main
1. Defining Media queries
One difference between style and sx which is most popular is, defining media queries based on the Material UI theme.
How would you deal with media queries in style prop? You can use your own breakpoints and do it the way you do in CSS for sure, but you cannot use Material UI breakpoints.
This is where sx prop comes in handy where you can define media queries and other MUI provided properties to alter your styles.
Example
import { Typography, createTheme, ThemeProvider } from '#mui/material'
let theme = createTheme()
function Demo() {
const stylePropCSS = {
backgroundColor: 'lightblue',
/* theme prop is not understood here,
so this breakpoint will not work,
and text will not turn into orange */
[theme.breakpoints.down('xl')]: {
backgroundColor: 'orange',
},
}
/* theme prop is understood here.
breakpoints can be applied here based on MUI theme
*/
const sxPropCSS = {
backgroundColor: 'lightgreen',
[theme.breakpoints.up('xs')]: {
backgroundColor: 'orange',
},
}
return (
<ThemeProvider theme={theme}>
{/* style prop */}
<Typography style={stylePropCSS}>
This text will <b>NOT TURN</b> orange as
I am using style prop and MUI 'theme' is not understood
</Typography>
<br />
{/* sx prop */}
<Typography sx={sxPropCSS}>
This text will <b>TURN</b> into orange as
I am using sx prop and MUI 'theme' is understood
</Typography>
</ThemeProvider>
)
}
export default Demo
2. Nesting styles and using theme inside sx prop
You can nest the styles when using sx prop, but can't do this when using style prop.
With sx prop
<Box sx={styles.post}>
<Typography variant="h4">This is the title</Typography>
</Box>
Box is a div of background black, and I need h4 to be yellow OR MUI primary color. With this requirement, I can nest my styles when I use sx prop like this where I place h4 inside post and define sx only on Box
const styles = {
post: {
backgroundColor: 'black',
// nesting h4 inside post
h4: {
// color:'yellow' // OR
color: (theme) => theme.palette.primary.main,
/* I cannot use theme inside style object. Since I am going
to apply styles.post to sx prop,I could make use of theme
object here as an argument */
},
},
}
With style prop
<Box style={styles.post}>
<Typography variant="h4" style={style.heading}>
This is the title
</Typography>
</Box>
const styles = {
post: {
backgroundColor: 'black',
},
// I can't nest the heading inside post, so defining it outside post
heading: {
color: 'yellow',
// color: (theme) => theme.palette.primary.main, // THIS WON'T WORK with style prop
},
}

Text seems to disappear when reaches a certain size

I have a small component and I seem to have an issue with the font size. When the fontSize is 179 the text loads correctly. When the font size is 180 and above it seems to vanish.
return (<View style={{flexDirection:'row'}}>
<Text numberOfLines={1} style={{
textAlignVertical: "center",
fontSize: 179,
textAlign: "center",
backgroundColor:'rgba(0,0,0,0)',
color:'rgba(0,0,0,.3)',
flex: 1,
flexWrap: 'wrap',
}}>A</Text>
</View>);
I can still see the element in dev tools:
Has anybody come across this before?
Am I just missing something?
Thanks, James
You shouldn't have numberOfLines={1} and flexWrap together; They contradict each other.
If you want it the text to wrap around, remove numberOfLines or have it be more than 1.
If you don't want text to wrap remove flex and flexWrap properties from the style object.
What is your goal with those styles? I recommend you start with unstyled (default) styled . Add styles one by one and see what works or doesn't work to give you your desired results.

Rendering equal width buttons in React Native

I want to render 7 buttons in a row with equal width to fill up all the available space horizontally.
render() {
return (
<FlatList
data={this.state.days}
renderItem={({ item }) => <View style={styles.button}><Button title={item.getDate().toString()} /></View>}
keyExtractor={item => item.getDate().toString()}
horizontal={true}
style={styles.list}
/>
);
}
const styles = StyleSheet.create({
list: {
display: 'flex'
},
button: {
flex: 1
}
});
They are inside a flatlist, wrapped in a view because I cannot style buttons directly.
In regular flexbox in an HTML page this approach works.
This what I get in React Native:
Maybe there are some flatlist behaviour I'm not familiar with?
First of all when you are styling in react native everything is already in flex so you don't have to do display : flex
If you want to render 7 buttons with the same width in a row make sure that all of them have the same parent. Then use flex:1 in each one of them.
What is happening when you do flex:1 when you put a number in front of flex in your styles in this particular situation it divides your parent into those many parts and give it to the child.
So all your button will have 1/7th of the width. If you put flex:2 in one of the styles then that button will have 2/7th of the width and the rest will have 1/7th.
Please feel free to ask for more clarity on the above said.
I got the same issue, I have added View component cover for each Button like the code below:
<View style={{flexDirection:"row"}}>
<View style={{flex:1}}>
<Button title="Add" />
</View>
<View style={{flex:1}}>
<Button title="Clear" />
</View>
//....
</View>
Have you tried adding flex: 1 to the list so that it takes up the entire horizontal space?
in my point of view, try adding justifyContent as space-between in the style object list
in each item ,set this style:
width:width/7

React Native - How to display content over background Image and Vertically center that content

This same question is previously asked by many people. The issue is that
Image component cannot contain children .If you want to render content on top of an image, consider using the <ImageBackground> component or absolute positioning.
I have referred the article Text Over Image and previous question on stack overflow . Content over image is working using <ImageBackground> component. But how to make it work using <Image> component instead?
Part 1 (Text / Body of your question)
To answer the text/body of your question, the official docs say:
A common feature request from developers familiar with the web is
background-image. To handle this use case, you can use the
<ImageBackground> component, which has the same props as , and
add whatever children to it you would like to layer on top of it.
Thus, <ImageBackground> component is specifically designed for ability to display content over a background image. So, you must use <ImageBackground> component for placing content over an image. This behavior cannot be achieved through <Image> component.
Part 2 (Title of your question)
Now, to answer the TITLE of your question, try this:
<ImageBackground
style={{ flex: 1, flexDirection: 'column', justifyContent: 'center', alignItems: 'center' }}
source={require('path/to/img')}>
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center', width: '100%' }}>
<Text>Centered Text (both vertically and horizontally)</Text>
</View>
</ImageBackground>
justifyContent works along primary axis (in this case column/vertical). alignItems works along secondary axis (in this case row/horizontal).
You may not need width: '100%' in child View, but I think that I've experienced some issues a couple of times. So you can experiment with that.

React-Native: How to increase space between text and underline

I have followed styles:
const styles = StyleSheet.create({
title: {
textDecorationLine: 'underline',
textDecorationStyle: 'solid',
textDecorationColor: '#000'
}
});
and it creates the underline for my content into some Text component. But it seems that this underline is too close to the text decorated with it.
Can I increase this distance in some how?
Thank you for helping!
Wrap your Text in a View that has a style containing borderBottomWidth: 1 or whatever you want the thickness to be.
Give your Text a lineHeight to adjust the spacing between the border and the content. If you have multiple lines of text, then using paddingBottom would also work.
Simple as that really. Bear in mind the View border will stretch to include any padding on the View itself.
As of now that is not possible in React Native, cause it is also not supported in web apps i.e Css.
Link here
But there is a work around to this.
Create react View wrapper over the Text you want to adjust the underline. And then add borderBottomWidth to View and adjust the distance of underline from Text paddingBottom.
const styles = StyleSheet.create({
viewStyle : {
borderBottomWidth: 10, // whatever width you want of underline
}
title: {
paddingBottom: 4, // Adjust the distance from your text view.
}
});
Add the viewStyle to your parentView.
Hope that helps!
According to me, the best possible way to do this is to add a <View /> (without content) after the <Text> and give top-border as you want your underline to be.
For example:
<Text style={{ color: 'blue' }}>Categories</Text>
<View style={{
height: 0, // height is '0' so that the view will not occupy space
width: 100, // as much as you want to 'Stretch' the underline
borderTopColor: 'blue',
borderTopWidth: 2, // 'Thickness' of the underline
marginTop: 15 . // 'Gap' between the content & the underline
}} />
REMEMBER: This will work if the parent of your Text has flexDirection: 'column' (which is default value). But if it has flexDirection: 'row', wrap both the Text & View (i.e. underline) in another view like <View>...</View> so the items will be arranged in a column.
How to give space between text and decoration line in react-native. Refer the attached image
1 with bottom border width and 2 is decoration line

Categories

Resources