I'm trying to style a component to stretch the whole width of the view. In Android this works by adding a style={{alignSelf: 'stretch', width: null}} in the component, however the same code on iOS does nothing. What would be a good workaround? Also any idea why they act differently?
I'm doing a button component that should work in various situations, so the idea is that the component itself should stretch (if the prop is given), instead of touching the container around it. So modifying the container's flex properties should be avoided, even though I could probably do it with something like justifyContent:stretch off the top of my head, but for this purpose let's rule this option out... Also because this is an app that is working now fine on Android and needs to be ported to iOS, so I'm trying to do as small changes as possible. I can't rewrite the entire code to work around this issues. I'd need a self-contained solution if possible...
Using RN 0.21. I know I should update but that's a headache that I'm saving for later...
Did you try add flex:1 to your component?
If your parent have any width and your only child flex: 1, then the child should have 100% width.
Related
I am learning react native for a few weeks and I made a screen and it looks good on my android emulator but when I look at it on my android phone somethings are out of place like icons or borders. I think It is about diffrent sizes of screens. Any idea how to fix this?
https://i.stack.imgur.com/rzhYn.jpg
https://i.stack.imgur.com/mhU2R.png
Yes, It could be.
You can avoid this in multiple ways.
Use dimensions, and get width and height based on the phone
https://reactnative.dev/docs/dimensions
or
https://www.npmjs.com/package/react-native-responsive-screen
For font size you can use this:
https://www.npmjs.com/package/react-native-responsive-fontsize
You have to make sure a few things while developing a component or when you style it:
Try to avoid fixed width/height or placement. Let the content grow the height of the box.
Either use flex or percentage width.
In your example, what I can see is the icon is going over the boundary of the box. To give a simple example, if you want to show a text on the left and say image on the right, use styles like this:
<View style={{display: 'flex', flexDirection: 'row', justifyContent:'space-between'}}>
<Text>Hi</Text>
<Image/>
</View>
When creating custom components with React Native, is it possible to tweak/access the standard props for the objects within that component without having to define them all inside of the component. I know that's a bit vague, so here's the example: I want to have a TouchableOpacity to use throughout my project with many standard style properties, so I will create a component defining those:
const ButtonStandard = props => {
return(
<TouchableOpacity onPress={props.onPress} style={styles.Buttons}>
<Text style={[styles.ButtonText, {color: props.TextColor}]}>
{props.ButtonText}
</Text>
</TouchableOpacity>
)
}
As you can see, the style of the touchable is defined by styles.Buttons, however the style of the text is defined by styles.ButtonText with the ability to change the text color if I need to through props.TextColor (see Edit below). But as the project grows, there may be a situation where I want the text size larger for one button/touchable. So I add "fontSize: props.TextSize" to the text style. And then another scenario pops up where I want to change the padding on the touchable so I add that.
And now we are back to the question: is it possible to avoid redefining every style property for this component (and any other) by some intrinsic method of overwriting the property where needed? Or is the only solution to keep adding props to cover overwriting the style properties as needed? I understand that at some point there's a line where a second or third or fourth component should be created.
Thanks for your help!
Edit: It seems things weren't even working the way I thought they were. I thought I was setting the font color to black and then giving myself the ability to change it with props.TextColor when using the component, but I realized what's actually happening is it completely ignores the styles."style" (in this case ButtonText) for that property, and sets the value to whatever is defined in the component instance OR the default value if the prop is not used in the component instance. I only thought it was working the way I described above because for text, the default value is black which prevented me from realizing it was ignoring the value in styles.ButtonText when I didn't use props.TextColor. My original question does still stand.
There are a few approaches I have come across (there can be many as well), sharing them here,
If you take any project, as per best practices in regards to UI/UX, you could use only very few types of buttons based on padding, width, color, disability, shape, and everything. You could consider any famous CSS libraries or react-component libraries they provide only a very specific set of button styles, it's because it is always recommended to have same type of button across the applications. Hence you could create a set of button types and let any parent component choose one.
I have also faced an issue, where I had a single text component for the entire project and suddenly new requirements came-up in a such that used text component couldn't handle. This time instead of allowing each property as single prop like instead of padding, margin, color as each one prop, I did expose one prop
called textStyle where parent component can pass the entire TextStyle and that ll be used to override.
for example,
// from parent component
// pass styles only if required to override existing styles
<Button
textStyle={{...anyStyleRelatedToText}}
buttonStyle={{ ...anyStyleRelatedToTouchable }}
/>
// button component
const ButtonStandard = props => {
return(
<TouchableOpacity
onPress={props.onPress}
style={[styles.Buttons, props.buttonStyle]}
>
<Text style={[styles.ButtonText, props.textStyle]}>
{props.ButtonText}
</Text>
</TouchableOpacity>
)
}
Back when I originally asked this question, I did not really know about or understand the spread operator ("..."). It turns out this is the key to achieving my goal. Some code:
style={[
styles.PrimaryStyles, {
backgroundColor: props.ButtonColor,
...props.SecondaryStyles,
}
]}
This is the style declaration for a custom button. Here, I am setting some PrimaryStyles for the button within the component file. These are things I don't expect to change very often at all such as the shadow or alignContent. Then, I include props.XXX for any properties I more or less want to require. What I mean by more or less is, if you don't specify a value for these props, they will default to their default values (if they can). So in this case, the backgroundColor would end up transparent (for iOS at least). And finally, the use of the spreader which allows us to access all of other props available for that component (in this case I was doing a button).
The way React Native implements these properties is in the order they show up. So if PrimaryStyles sets the backgroundColor to red (for no good reason in this situation), and then props.ButtonColor is green, the button will be green. A more applicable example is if PrimaryStyles sets the width to 20% of the screen width, but I have a button I want to be bigger than the others, I can do the following in the instance of that specific button:
<ButtonRectangle>
ButtonColor={ColorScheme.Orange.e}
ButtonText={'Roll Again'}
SecondaryStyles={{
width: Dimensions.get('window').width * 0.5
}}
TextSize={20}
onPress={() => ExerciseGenerator()}
/>
I'm sharing this because it's the best way I figured out how to achieve the functionality in the question. But I'm still a very inexperienced programmer and welcome any feedback on this method.
I have a webview, and I set it's html like this:
<WebView source={{ html:body }} style={{ width:400; }} scalesPageToFit />
However on load, the page within is zoomed at 100% so its outside of the boundaries. Is it possible to start the zoom at a level so it fits perfectly inside the boundaries?
Here is a screenshot of what it looks like right now: https://i.imgur.com/i4nRnA0.png
I want it to start zoomed so all content fits like this: https://i.imgur.com/MtiyXMe.png (i manually pinched to zoom out here)
I thought the scalesPageToFit prop would do this, but its not doing anything, whether true or false. Does anyone know what scalesPageToFit does?
Thanks
scalesPageToFit avoids your web page to outscale in the webView, so that its minimum zoom does not show empty spaces.
Since React Native Docs version 0.52 a new prop initialScale is provided and seems to be exactly what you're looking for. Unfortunately it has never been added to the code: an issue is still open.
So, in order to achieve your objective, you have to use nativeConfig to override the native component behavior: what I mean is fully explained in these two examples:
First
Second
I have a similar WebView with a header and style={{flex : 1}} achieves this for me.
In React-native 0.39,
I often get this error but I don't really understand why (appart the fact that I didn't set flexGrow...).
Here is the error from the console
View was rendered with explicitly set width/height but with a 0 flexBasis. (This might be fixed by changing flex: to flexGrow:) View: <RCTShadowView: 0x6080005a02a0; viewName: RCTView; reactTag: 2608; frame: {{0, 0}, {nan, nan}}>
Can someone explain to me why this error is triggered ?
EDIT: The error comes from this bit of boilerplate code I added in on the main View :
flex: 1,
// remove width and height to override fixed static size
width: null,
height: null,
But If someone could shed a light on react-native styling or lead me to good resources, I'll be grateful :)
Refer to react-native/React/Views/RCTShadowView.m, there has comments
This works around a breaking change in css-layout where setting flexBasis needs to
be set explicitly, instead of relying on flex to propagate.
We check for it by seeing if a width/height is provided along with a flexBasis
of 0 and the width/height is laid out as 0.
The error is triggered when flex without defined flexBasis and having either width and height is zero.
Refer to react-native/Libraries/StyleSheet/LayoutPropTypes.js, we know the flexGrow, flexShrink and flexBasis receive values of number.
For visual guide, you might refer to this webpage, A Visual Guide to CSS3 Flexbox Properties.
Specifying flexDirection: 'row' solves the problem ¯\_(ツ)_/¯
As the error states - change the 'flex' to 'flexGrow' and the issue is solved.
The difficult part about this is how to locate which 'flex' needs to be changed from the whole project. What I did was (using Atom Editor, so 'Find in Project' ...) changing each 'flex:' with 'flexGrow:' (with the ':' at the end you make sure that you change a style) and then simply search the whole project again for a 'flexGrow' and change every 'flexGrow' from each file with 'flex' until you locate the corrupted 'flex'.
I prefer using flex instead of flexGrow, that's why I revert to flex. This is why: Layout Props
Hope this solves the issues.
In node_modules/react-native/React/Views/RCTShadowView.m add "//" at line 170 like this "// RCTAssert(!YGNodeIsDirty(node), #"Attempt to get layout metrics from dirtied Yoga node.");"
Figure, why setting the justifyContent property does not work?
When you are in a container that has the flex direction set to 'row' then justify-content actually sets the location horizontally instead of vertically. So in this case you would use alignItems. Since the RN implementation of flexbox is pretty close to the standard baring a few naming differences ('flexDirection' instead of 'flex-direction', etc) I would recommend working your way through the tutorial at http://flexboxfroggy.com/. Level 10 actually shows an example of the issue you are encountering here.
try to add alignItems: 'center'