React navigation header resizes on navigating to a screen with react-navigation/native-stack - react-native

I'm getting a strange behavior when navigating into a screen containing the code below.
I believe it has something to do with how the flex containers are rendered when navigating into a new screen, but I'm not sure.
This seems to happen when creating navigation stacks with '#react-navigation/native-stack', and once for the entire navigation regardless. See EDIT 2 below for more details.
export const AccountScreen = ({ route, navigation }: any) => {
return (
<View style={{ flex: 1, flexDirection: 'column'}}>
<View style={{ flex: 1 }}>
<Text>Top</Text>
</View>
<View style={{}}>
<Text>Bottom</Text>
</View>
</View>
);
};
I created a snack to demonstrate: https://snack.expo.dev/#kickbk/navigation
The issue is that when I navigate to Account, you may notice that Bottom sort of jumps up the first time it renders the screen. It's very brief, but it happens.
This glitch becomes more visible when adding content such as images instead of Text.
On a very bad Android phone it sometimes gets stuck where the Bottom part gets hidden below and behind the navigation bar.
If you look at this snack and go to Carousel.tsx, you will see the content I load into Screens > AccountScreen.
How do I do this correctly? How do I load this screen so the content will not jitter like that?
MORE INFO
I screen recorded what I was seeing and you can clearly see it jmuping around, frame by frame:
Frame 1
Frame 2
Frame 3
Frame 4
EDIT 2
It looks like this header adjusting height behavior happens with #react-navigation/native-stack. When switching to importing stack from #react-navigation/stack, and then navigating to Account, the glitch doesn't happen.
However, when launching the app, despite all the stack getting imported from #react-navigation/stack, the header gets adjusted once. I am guessing it's because the entire JS implementation of stacks sits inside a native component which cuases this behavior when loaded once. Maybe caused by NavigationContainer from '#react-navigation/native';?
I have updated the snack (https://snack.expo.dev/#kickbk/navigation) with a minimal implementation so there's no doubt on possible third party dependency influencing this.

Related

How to manually set the app window dimensions in React Native

I was wondering if it would be possible to manually set the entire app window dimension manually.
I'm aware of the useDimensions hook but I'm not interested in listening to window's dimensions changes, I'm interested in setting them.
For example I would like to set an entire app to be 50% in height of the available screen size.
I could't find any doc on this specific request.
Is this possible? How?
why dont you try this in the root level file, like app.js or somewhere ,
const App = () => {
return(
<View>
<View style={{height:0.5*Dimensions.get('window').height}}>
<StackNavigatrScreen />
</View>
<View style={{height:0.5*Dimensions.get('window').height,width:'100%'}}/>
</View>
)
}
and inside the stack navigator you have your enitre screens, so that it just occupies 50 percent of screen ,rather than whole
hope it helps, feel free for doubts

State update only affects component if it affects parents' style

I have an app that lets the user scan for and connect to BLE devices. If the user taps a scan result, I want a spinner to show next to the device while the connection is in progress.
I am using Redux to keep track of discovered and connected devices. The main App component is connect with redux-react and passes the relevant bits of state to its children.
One of these children is the list of scan results, which in turn passes one device to each of its entries. The scan list items look like this:
class DeviceListItem extends Component{
render = () => {
return <TouchableOpacity onPress={this.props.onPress}>
<Card>
<View style={{justifyContent: "space-between", flexDirection: "row", flex:1, width:"100%", alignItems: this.props.item.pendingActions !== 0?"flex-start":undefined}>
<Text>{this.props.item.name}</Text>
<ActivityIndicator animating= {this.props.item.pendingActions !== 0} color={Colors.accent} size="small"/>
</View>
</Card>
</TouchableOpacity>
}
}
Now, in the style of the View component you might notice this peculiar snippet of code:
alignItems: this.props.item.pendingActions !== 0?"flex-start":undefined
For some reason, this is required to make the spinner show up when the state of the device changes. It seems that in order to make the spinner show up on a state update is to make the style of the parent view depend on the state. It doesn't seem to matter which property changes, but it needs to change in some way in order for the spinner to be affected by the update.
Does anyone have an idea about what's going on here? I'm still fairly new to Redux and React Native so there's a chance I am misusing something here but this just seems odd to me.
When updating props the component isn't self-aware about props changes.
When you update the parent state, and the props has been changed, it will trigger a render at the parent, that will also re-render the child you are talking about.
When updating a prop value, without triggering a render, the child will not re-render.
To make it aware about props changes you have to use componentDidUpdate.
An example would be:
componentDidUpdate=(prevProps)=>{
if (prevProps!==this.props)
this.setState({propsChanged:!this.state.propsChanged})
}
To use the example above, you have to define a state variable called propsChanged.
Hope this helps you understand the problem and find a solution that suits you!

Scrolling to the bottom of a ScrollView on initial load

I have a large long image with a few inputs at the bottom in my react native view. When the view loads I want it to be "scrolled" to the bottom of the page -- all the examples I can find online are triggered because of some event that the user does.
I want to call scrollToEnd() but I'm not sure where in the lifecycle the ref will actually be set -- anyone have ideas?
Use scrollToEnd this way. It will take to the bottom of ScrollView
<ScrollView
ref={ref => this.scrollView = ref}
onContentSizeChange={(contentWidth, contentHeight)=>{
this.scrollView.scrollToEnd({animated: true});
}}>
I am not clear with your question but
With react-native Image component we have onLoad prop. I think this is what you are looking for.
try something like:
<Image source={{uri...}} onLoad={(e)=>this.handleImageLoaded(e)}/>
then inside handleImageLoaded method you can use scrollToEnd() with some ref as per your code.
also there are other useful props like onLoadStart/End check here
https://facebook.github.io/react-native/docs/image
Or if you are just waiting for the view to render then to scroll
for that I think componentDidAppear() lifecycle method, if using react-native -navigation by wix
or with react-navigation
willFocus, willBlur, didFocus and didBlur events for the component render cycle.. explained here
https://reactnavigation.org/docs/en/navigation-prop.html#addlistener-subscribe-to-updates-to-navigation-lifecycle

react-native - TouchableHighlight: Remove highlighting after onPress?

I am developing a simple react-native app and am encountering an issue on TouchableHighlight:
When pressing the TouchableHighlight, a new screen is displayed (using StackNavigator from react-navigation). After pressing the back-button and returning to the original screen, the TouchableHighlight still has a black background-color - meaning, that it is still highlighted.
My questions are:
Is there a way to manually deactivate the highlighting of a TouchableHighlight-component? That way I could disable the highlighting after onPress has run.
What could be possible reasons to why the TouchableHighlight stays highlighted? I am using it on other parts of my app without navigation, and I could imagine that it has to do with that.
The TouchableHighlight exists within a FlatList. The renderItems-method looks like the following:
let handlePress = () => {
this.props.navigation.navigate('DetailsScreen');
};
return <TouchableHighlight
onPress={handlePress}>
<Text>Some Text</Text>
</TouchableHighlight>;
If you need/want any further information, please let me know. I've tested the code on android, using the Genymotion-emulator with Marshmallow.
Versions are:
node -v: 8.9.4
npm -v: 5.6.0
react-native-cli: 2.0.1
react-native: 0.54.2
react-navigation: 1.5.2
Build environment: Windows 10 64-bit
At this point, I'm quite certain that the error is somewhere in my code, as TouchableHighlight works correctly on other parts of my app, and it propably has to do with the navigation-call, but I was unable to pinpoint, why exactly. I've made sure that there are no exceptions or anything like that in my app, and that the onPress-method therefore finishes successfully.
You can replace Touchable Highlight with Touchable opacity and simply set activeOpactity prop with value 1. It will not highlight the press.
<TouchableOpacity activeOpacity={1}>....</TouchableOpacity>
After using the tip from #Kartiikeya and exchanging TouchableHighlight with TouchableOpacity and back to TouchableHighlight, it now works as expected:
Now, after onPress has been executed, the button (be it a TouchableOpacity or a TouchableHighlight) looses its effect.
I am not sure, why it works now. The obvious reason would be, that a recompilation of the source code fixed errors - but I recompiled it for writing the original question multiple times before, so that that cannot be an option. Other users I would suggest to clear any cache possible, and especially do the following steps:
Close and reopen the android emulator / restart your testing device
Restart the build PC
Recompile all source code
Check in your console for errors and/or exceptions (obviously)
Replace TouchableHighlight with TouchableOpacity, recompile, check if the error still exists - and if not, reexchange TouchableOpacity to TouchableHighlight
You can replace Touchable Highlight with Touchable opacity. It won't highlight the press.
return <TouchableOpacity
onPress={handlePress}>
<Text>Some Text</Text>
</TouchableOpacity >;
For me, i needed to disable the highlight effect after onLongPress has been fired. You can simply change the key of the touchable using a re-render when you want to remove it.
Here's an example:
<TouchableHighLight
onPress={this.pressRow}
style={styles.outerContainer}
onLongPress={() => this.setState({ onLongPressed: true })}
onPressOut={() => this.setState({ onLongPressed: false })}
key={`long-pressed-${this.state.onLongPressed ? 'yes' : 'no'}`}>
<View style={styles.innerContainer}>
{rowText}
{rowIcon}
</View>
</TouchableHighLight>
Following Leonardo Lusoli's answer, there one thing you should also add is
useEffect(() => {
if(isLongPressed){
setIsLongPressed(false)
}
}, [isLongPressed])
This step is necessary because
when the first onLongPress event is fired it will set isLongPressed to true and thus changing the key the component is re-rendered and is identifies as a new component and previour event listners are discareded so the onPressOut will not be fired. So
when isLongPressed is set to true the component re-renders and then immediatietly we set it's value to false and thus we get the expected behaviour. Otherwise we will get the unexpected behaviour followed by one expected behaviour.

TouchableHighlight and TouchableOpacity get highlighted on render()

I experience a behaviour where TouchableHighlight and TouchableOpacity reacts visually upon render (onPress is not being called).
One thing is that it looks just a little strange, when I enter the page and my button make a small "blink". This is strange but tolerable. The more frustrating part is that if I alter state for the parent component and thus invoke a re-render(), the button will "blink" again, making all buttons blink whenever I alter state.
Pushing the buttons alters page state, and thus pushing a button makes both buttons "blink".
I use react-redux, but this should not affect this behaviour.
The code below is just for illustration.
render()
{
return(
<View>
<ToucableHightlight> //Click here changes state
<Content/>
</ToucableHightlight>
<ToucableHightlight> //Click here changes state
<Content/>
</ToucableHightlight>
<View>
);
}
Add activeOpacity in TouchableOpacity and it will force to not blink.
<TouchableOpacity style={styles.opecity} activeOpacity={1}>
I solved the problem. Earlier during my render function i defined the "Content"-components, resulting in new (but alike) components being defined during each update. Placing the definitions of "Content" outside of the render function fixed it, so that the components no longer flashes when the page is re-rendered.
This explains why my component was rendered as a new component upon each render in the parent component, but it does not explain why a TouchableHighlight blinks during its initial render.
Buttons blinking during initial render is acceptable to me - buttons blinking upon any state-change is not.
So I am sufficiently happy now.
Not sure if it's because I'm running a later version, but I found this blinking behavior happens only on the first click.
My solution was putting the code that triggers rerendering in a setTimeout
<TouchableOpacity
onPress={function() {
setTimeout(function() {
_this.setState({myState: 'someValue'})
});
}}
>