How to show View within styled (using styled-components) View - React Native - react-native

I am learning React Native and styled components. I am working on a simple iOS app and facing some problems with styled-components.
What I'm trying to do
I am trying to show modal on click which looks like this
<Modal visible={this.state.isModalVisible} animationType={'fade'}>
<StyledView flex={1} padding={10} backgroundColor={'orange'}>
<View>
...more Views and Texts
</View>
</StyledView>
</Modal>
StyledView is a custom view that I have created using styled-components which looks like this
const ViewWrapper = styled.View`
flex: ${props => props.flex};
padding: ${props => props.padding};
backgroundColor: ${props => props.backgroundColor};
`;
const StyledView = ({ flex, padding, backgroundColor }) => (
<ViewWrapper
flex={flex}
padding={padding}
backgroundColor={backgroundColor}
/>
);
export default StyledView;
Problems I'm having
1) When I set padding={10}, I get an error Failed to parse declaration "padding: 10".
2) After Googling a bit, I found that I should be using padding={'10px'} which throws this error, 10px is of type NSString cannot be converted to YGValue. Did you forget the % or pt suffix?.
(padding={'10%'} works fine)
Then I simply tried setting flex and padding values in ViewWrapper and send only background color as prop.
3) But for some reason, Views and Texts nested within StyledView does not show up.
Please tell me why it's not working and help me understand what I'm missing here. Thanks.

You have a couple of issues.
Styled Components do not accept strings in them, so you cannot
sent the '10px' from the prop.
You are correct that padding
needs a px at the end. Something padding alone does not work, but
you can workaround it by adding paddingVertical and
paddingHorizontal with the same value which is the same as padding.
You are overcomplicating the implementation as you dont need to
pass the style props and you can define them all within your styled
component. Like this:
import styled from "styled-components/native"
const StyledView = styled.View`
flex: 1;
padding: 10px;
backgroundColor: orange
`;
And then you just use it like this:
<Modal visible={this.state.isModalVisible} animationType={'fade'}>
<StyledView>
<View>
...more Views and Texts
</View>
</StyledView>
</Modal>
No need for the ViewWrapper or more props. Also, and this is just personal. I only use StyledComponents for the ones that could change at run time or depend on a theme, like exts for fontFamily or fontSize. For the rest that have constant styles that never change, I just use normal styles objects since it is less verbose.
If this is a simplified version and you absolutely need the props you con move them all into one single theme object and pass it with a ThemeProvider and then just read it as ${props.theme.backgroundColor} or something.
Cheers

Related

React native size, does it scale automatically depending on the size of device?

I'm new to react native, and I have a question. Does react native scales the sizes of components depending on the device or what will be the best way to do it if it does not?
You can use some libs to help with this, like: react-native-responsive-fontsize
Just config the width/height of your component with RFValue as:
import { RFValue } from "react-native-responsive-fontsize"
export function Welcome(){
return (
<View style={{width: RFValue(50)}}>
<Text style={styles.welcome}>Hello</Text>
</View>
)}
const styles = StyleSheet.create({
welcome: {
fontSize: RFValue(24)
textAlign: "center",
margin: RFValue(10),
},
});
You don't need to use this on every component or measure/size
You can use flexbox in react-native that will scale the components automatically to fill the space.
If you want to do more advanced style changes for different sizes will need more advanced solutions. I really like the library restyle by shopify they have some solutions, but most likely flex will be all you need.
https://github.com/Shopify/restyle
React Native by default doesn't scale component sizes. However, there are a few things you can implement while styling to make sure it looks similar on all devices:
give widths and heights in percentages instead of numbers
use flex for views. this allocates a percentage of the screen to that component.
try to use alignItems and justifyContent as much as possible instead of giving values for aligning manually.
use RFValue from react-native-responsive-fontsize, which scales according to screen size.
This should make the app look similar on all devices

React Native text input is not responding to touch and not focusing when in multiple Modals

Our project requires us to use a modal that is inside another modal(I know it`s bad but it was not my decision :) ) and it seems like text inputs are unable to focus when they are inside nested modals?
Here is a super simple demonstration of a code that fails to focus on text input when placed inside nested modal.
import React, { useState } from "react";
import { Modal, StyleSheet, View, TextInput } from "react-native";
const App = () => {
const [text, setText] = useState('Some text');
return (
<View style={styles.centeredView}>
<Modal visible={true} transparent={true} >
<Modal visible={true} transparent={true} >
<TextInput value={text} onChangeText={setText} style={styles.textInput} />
</Modal>
</Modal>
</View>
);
};
const styles = StyleSheet.create({
centeredView: {
flex: 1,
justifyContent: "center",
alignItems: "center",
marginTop: 22
},
textInput: {
width: 300,
height: 300,
backgroundColor: '#00ffff',
}
});
export default App;
Here is an expo demo of this code running
Is this something that is not supported at all or it`s possible to somehow force focus on the text input when clicked?
Okay, it seems like there's some sort of bug.
My best guess would be the following:
A modal will only allow you to focus on the components within that modal. Which makes sense, when using the Modal as a pop-up.
However, when you nest these Modal components, there is some issue figuring out whether the <TextInput> should be accessible or not.
Here's an adjusted version of yours: https://snack.expo.dev/qM68rALpM
Here, we can see that the <TextInput> does work within one <Modal>, but not within two. It won't work for 3 either.
So,
Our project requires us to use a modal that is inside another modal
Really? Does it require you to? Considering this is bugged behaviour - and it may not be a known one, stating that this solution is "required" is not something I would agree with. I suggest approaching this in a different way, as I suspect what has happened here is trying to solve X with Y, but Y isn't working and you want to solve Y.
If you want, you could elaborate on the problem (X) that you are trying to solve in the first place. It may be solved by having 2 <Modal>'s, but not nested, etc.
Ok, figured out what was going wrong.
I am not entirely sure what is causing this but if the two modals open at the same time, text input is unable to focus.
If however, the inner modal is shown after the first one was already open, it works just fine. Take a look at these two examples to see the difference in behaviour.
Modals open at the same time. Focus is not working
Second modal is opened with some delay(Even works without the delay as long as it opens a bit later than the first one)

React Native Flex Child Growth Seemingly Affected by Padding

I am experiencing some difficulty when using flex for layout in React Native. I have reduced the behaviour I am experiencing down the the following problem.
I have one container which fills the available space since it has flex set to 1. I then have a child within that container which should fill the container, so I have set it's flex to 1. I also want the container to have padding at the top. I would therefore expect the child to fill all of the parent apart from the top 20%. However, what I am seeing is that there is equal padding at the top and bottom of the container around the child (as if I had set paddingVertical instead of paddingTop).
Is anyone able to explain why this behaves as it does? I am also intrigued by the behavior I saw when creating a snack to illustrate this problem. On the iOS and Android simulators I am experiencing the behaviour which seems erroneous to me whereas the Web view seems to work as I would expect with only a gap at the top.
Thanks!
Snack example code
import * as React from 'react';
import { Text, View, StyleSheet } from 'react-native';
export default function App() {
return (
<View style={styles.container}>
<View style={styles.child}>
<Text>
Change code in the editor and watch it change on your phone! Save to get a shareable url.
</Text>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'green',
paddingTop: '20%'
},
child: {
flex:1,
backgroundColor:'red'
},
});
Android - 'broken' behaviour
Web - 'expected' behaviour

visibility hidden on react native not working, to take space even not shown?

I have TouchableOpacity in a space between flex container that I want to take space even not shown,
My code:
<TouchableOpacity
style={showClear && { visibility: 'hidden' }}
onPress={() => this.props.clearCompleted()}>
<Text>Clear Completed</Text>
</TouchableOpacity>
display: none works but it doesn't take space, the code above dont work but does in web?
In my case, I needed to use the element, so I did something like this:
<TextInput style={{opacity: 0, height: 0}} {...props} />
I hope this works for someone else with my problem.
Update
React Native's StyleSheet now supports toggling visibility using display: 'none' and display:flex.
Not all CSS are supported in React Native, that include visibility: hidden or display:none.
To hide a component, not to render it at all, render empty View or null. Or you want to switch a component visibility, verify react's state
<View>
{ !showClear && (
<TouchableOpacity
onPress={() => this.props.clearCompleted()}>
<Text>Clear Completed</Text>
</TouchableOpacity>
}
</View>
showClear is kept in state
As Leu mentioned, you can just render null.
Another option if you want to keep the area used by TouchableOpacity is setting up opacity: 0.0 in style, but then you have to remember to set also disabled={false} in props of the TouchableOpacity, to avoid call clicking action on invisible area
Just setting the opacity to 0 was enough for my use case.
If we want to make sure element takes space on DOM but is kept hidden to user,
Step 1: Hide it from user using opacity: 0 .
Step 2: Disable interactions of hidden element.
Our JSX should be like below:
<IconButton
icon="close"
disabled
style={styles.hiddenElement}
onPress={() => console.log("Pressed")}
/>
Our stylesheet should look like below:
const styles = StyleSheet.create({
...
hiddenElement: {
opacity: 0,
},
...
});

How to override style in NativeBase?

In every code example mentioned in NativeBase Docs, there's no usage of React StyleSheet.
I didn't find a way to override styles of NativeBase.
How can I include React StyleSheet into my app?
NativeBase uses a different approach to add styling to your component.
It uses a Utility first approach, wherein it leverages the ReactNative stylesheets API and adds a layer of Utility Props on top of it. The idea is to pass props to your component to change their styling, instead of defining a className and adding styles in a separate stylesheet.
For example, with React Native, you would write:
function Example() {
return (
<View style={styles.container}>
{/* content */}
</View>;
);
}
const styles = StyleSheet.create({
container: {
backgroundColor: '#0891b2',
paddingVertical: 16,
paddingHorizontal: 12,
borderRadius: 5,
alignSelf: 'center',
width: 375,
maxWidth: '100%'
}
});
Now, with NativeBase, to achieve the same view, you would write it as follows:
function Example() {
return (
<NativeBaseProvider>
<Box bg="primary.600" py="4" px="3" rounded="md" width={375} maxWidth="100%">
{/* content */}
</Box>
</NativeBaseProvider>;
);
}
Here, NativeBase has already defined bg, py, px, etc. as props and mapped them to the styling properties of backgroundColor, paddingVertical , paddingHorizontal, etc. respectively.
There is a list of these mappings provided here, which you can simply refer to add any styling that you may need for your component.
For NativeBase usually if you want customize the appearance you need to do it at a theme level and not a component/widget level.
http://nativebase.io/docs/v0.5.2/customize
I believe the reason it this keeps the UI consistent.
Have a look at the source code for all the widgets that you are trying to override the style for:
https://github.com/GeekyAnts/NativeBase/blob/master/Components/Widgets/
There are certain cases where you can amend certain aspects e.g. for the button
this.props.style is used anywhere but this.props.textStyle
So you can override the textStyle when you define the component:
<Button textStyle={{...}}/>
https://github.com/GeekyAnts/NativeBase/blob/master/Components/Widgets/Button.js