Header inset too big, even though I use the `forceInset` prop with insets provided by `react-native-safe-area-context`? - react-navigation

Problem
For some reason, when testing on my older Android phone (Android v 6.0.1), my Expo SDK 36 app's header always has extra padding, looking too bulky, even though the phone has no top notch.
Testing on an iOS 13.4 simulator for the iPhone 11 with a notch, it handles the notch correctly.
Some Debug Results
When I traverse the component hierarchy in react-native-debugger, I see that:
the <SafeAreaView /> in my Screen component from react-native-safe-area-view has the correct inset values from context, { top: 0, bottom: 0, left: 0, right: 0 }
However, the <Header /> component from react-navigation 4.3.7 has safeAreaInsets: { top: 'never' }, when I examine its props.scene.descriptor.options to look at its navigationOptions
And further, if I go into <Header/>'s children, when I get to <SafeView/>, its props.forceInset.top is 'always'.
I upgraded react-navigation today to 4.3.7 from 3.13.0, I'm wondering if there's bugs from earlier versions of dependencies like react-native-safe-area-context and react-native-safe-area-view, or bugs with them in general.
In package-lock.json, I notice that there's an #react-navigation/native package with a dependency of react-native-safe-area-view at version 0.14.9 exact, while I notice in package.json, I have react-native-safe-area-view at semver range ^1.0.0. I think I installed it via the expo install command for packages compatible with your Expo SDK, when first looking into the SafeAreaView instructions in the Expo docs
My render method looks like this:
render() {
return (
<SafeAreaConsumer>
{(insets) => {
console.log("render -> insets", insets)
return (
<SafeAreaView
style={{
flex: 1,
backgroundColor: Color.white,
paddingBottom: insets.bottom,
paddingLeft: insets.left,
paddingRight: insets.right,
}}
forceInset={insets}
>
<View style={MainAppViewContainers.container}>
...
</View>
</SafeAreaView>
)
}}
</SafeAreaConsumer>
);
}
Possible Explanations Coming To Mind
Could <SafeAreaView/> fail to automatically detect and set the right insets? Here it says, "By default, the device's safe area insets are automatically detected."
Could using the forceInset prop on <SafeAreaView/> fail to change the forceInset prop on <SafeView>, which is a child of the <Header/> component automatically generated by react-navigation once you set the correct header-related navigationOptions on screens/navigators?
Could there be some kind of dependency clash between the version of react-native-safe-area-view 0.14.9 used by #react-navigation/native 3.7.11 which react-navigation 4.3.7 installed, and react-native-safe-area-view 1.0.0 installed by Expo?

Related

When to use ResourceSavingView?

Looks like an interesting option for performance reasons. But I cant' find any guide, benchmark or motivation. Does anyone know when we should use ResourceSavingView?
If you look in the react-navigation source, ResourceSavingView is only used in a component called MaybeScreen. The purpose of this component is to use react-native-screens if it is available or fallback to a ResourceSavingView if it is not available, as you can see here:
export function MaybeScreen({ visible, children, ...rest }: Props) {
if (Screens?.screensEnabled?.()) {
return (
<Screens.Screen activityState={visible ? 2 : 0} {...rest}>
{children}
</Screens.Screen>
);
}
return (
<ResourceSavingView visible={visible} {...rest}>
{children}
</ResourceSavingView>
);
}
In other words, if you are using react-native-screens, ResourceSavingView will never be used internally by react-navigation.
The actual ResourceSavingView component is fairly simple:
<View
style={[styles.container, style]}
// box-none doesn't seem to work properly on Android
pointerEvents={visible ? 'auto' : 'none'}
>
<View
collapsable={false}
removeClippedSubviews={
// On iOS, set removeClippedSubviews to true only when not focused
// This is an workaround for a bug where the clipped view never re-appears
Platform.OS === 'ios' ? !visible : true
}
pointerEvents={visible ? 'auto' : 'none'}
style={visible ? styles.attached : styles.detached}
>
{children}
</View>
</View>
As you can see, it's basically just enabling some performance features when the component is visible.
The main purposes seem to be disabling pointerEvents and hilariously moving the contents of the view FAR_FAR_AWAY:
const FAR_FAR_AWAY = 30000; // this should be big enough to move the whole view out of its container
// ...
const styles = StyleSheet.create({
// ...
detached: {
flex: 1,
top: FAR_FAR_AWAY,
},
});
So, to answer your question, as far as I can tell it is intended to be used to improve performance when you need a View to be rendered but not always visible. This is something react-navigation needs because things like transition animations require that Screens remain mounted even when they aren't visible.
Of course, depending on your situation, you can always use pointerEvents and weird style hacks directly instead.
Personally, I would avoid using ResourceSavingView. I wouldn't be surprised if ResourceSavingView is eventually deprecated, since it is very clearly used as a fallback. (The file it's used in is actually called ScreenFallback.tsx.)

Failed to set an indexed property on 'CSSStyleDeclaration'

I have a project with Gatsby where I use React Spring. I am getting the following error:
TypeError: Failed to set an indexed property on 'CSSStyleDeclaration': Index property setter is not supported.
In some of my research I have found that browser updates have been causing this type of error in other scenarios, but I haven't found any that deal with React Spring.
I have tried upgrading all related dependancies to the latest versions.
I have narrowed it down to a part of my code that uses React Spring:
const trail = useTrail(2, {
opacity: sideNav ? 1 : 0,
x: sideNav ? 0 : 180,
height: sideNav ? 180 : 0,
from: { opacity: 0, x: 20, height: 0 },
});
<NavItem style={trail}>
<Link to="/">Home</Link>
</NavItem>
If I take out style={trail} I no longer get the error so I'm thinking it has something to do with React Spring's useTrail.
I expect it to work as it did a few days ago, where I don't get an error and my animation works as expected. I actually have not touched the code in a few weeks, and the site was loading correctly just a few days ago. Now it suddenly breaks.
The useTrail documentation says:
The return value is an array containing animated props.
Hence you can't directly apply trail to the style of one item. It should be something like:
{trail.map(style => (
<NavItem
style={style}
>
<Link to="/">Home</Link>
</NavItem>
))}

How to improve FlatList render performance for large list with ReactNative 0.43?

I am trying to render a list of ~250 images in 3 columns using FlatList in RN0.43, and I change the width of the images in the onLayout function of the FlatList to fit the width of screen.
The initial performance is ok, but after some scrolling up/down, sometimes it takes a second or 2 until images are shown.
it is even worse if I change to screen orientation, it takes 2~3 seconds to get screen updated.
a few findings:
after screen rotation, it takes a second or 2 until FlatList.onLayout is called
after FlatList.onLayout and update of image width, each image (about half of the list, ~150 images; while only ~15 are shown) is rendered 2~4 times, while render() is only called once.
question:
how can I modify the code to improve the performance?
in the getItemLayout() of a multicolumn list, should the offset be something like (itemHeight + separatorHeight) * (index%numColumns)?
Thanks.
tested on: GalaxySII (4.1.2) and Android SDK emulator (7.1.1)
var data = [
require('./res/img/Search.png'),
require('./res/img/test - Copy.png'),
// ~250 items
...];
class app extends React.Component {
renderItem (info, width) {
console.log('renderItem', info.index);
if(width !== this.width) {
this.imageStyle = {width: width-MarginHorizontal , height: width-MarginHorizontal, resizeMode: 'contain'};
}
return (
<Image
source = {info.item}
key = {info.index}
style={this.imageStyle}/>
);
}
render() {
console.log('Test.render');
return (
<View style={{
flex: 1,
flexDirection: 'row',
justifyContent: 'flex-start',
alignItems: 'center',
backgroundColor: '#F5FCFF'
}}>
<GridList
numColumns={3}
columnWrapperStyle={{ alignItems: 'center', marginVertical: 5, justifyContent: 'space-around'}}
data={data}
renderItem={this.renderItem}
/>
</View>
);
}
}
class GridList extends Component {
onLayout(event) {
console.log('GridList.onLayout()');
let newColumnWidth = event.nativeEvent.layout.width/ this.numColumns;
this.layout = Object.assign({},event.nativeEvent.layout);
if( undefined === this.columnWidth || Math.abs(newColumnWidth - this.columnWidth) > WidthTolerance ) {
this.columnWidth = newColumnWidth;
if(this.isComponentMounted) {
this.setState({renderCount: this.state.renderCount+1});
} else {
this.state.renderCount +=1;
}
}
}
render() {
console.log('GridList.render()');
return (
<FlatList
{...(this.modifiedProps)}
renderItem={(info) => { return this.props.renderItem(info, this.columnWidth); }}>
{this.props.children}
</FlatList>
);
}
}
Disclaimer: I know that the question is old, but here is my response anyways.
My app has a hand full of lists with 500+ items. So, we got to a point where the app was crashing on popular not-bad phones. Then I've made this extensive research about performance on FlatLists.
The FlatList component was presented as a alternative for the old ScrollView. The problem is that ScrollViews render all your list at once so they perform visually better, but there is a trade off in memory consumption, that leads to app crashes.
So Flat Lists are a necessary evil. They essentially only render items that are visible, which is a huge gain on memory consumption, but a pain for visual performance, specially for heavy/complex items, that happens to be your case with those responsive images.
How to workaround?
There are a lot of strategies that you can implement to mitigate your problem.
Use cached and performatic images, such as react-native-fast-image. Every operation that you can remove or abbreviate for freeing the Javascript thread: do it (every image is a new Image(), so, if they are cached, you have your loaded hook called sooner)
Your list item component is a read-only component, which is supposed to be 'dumb'. Implement a shouldComponentUpdate() { return false } or a more solid update control method as needed. This is HUGE perf boost.
Remove console.logs anywhere near your list. They slow the Javascript thread really bad.
Build your app for production and test it. It becomes almost always twice or three times faster. Dev env is slow because of debugging.
Give this article a good read for more strategies.
Conclusion
FlatList IS a slow component. This is a known, open and well documented issue. Do what you can to improve it, and let's hope future releases may fix this.
Yes I was having the same problem - multiple images & videos in list in react native So I removed Flatlist instead of this I preferred to use ListView to render fast & to fix touchability issue on list item but Dont forget to set PureComponent to the list item
You're re-creating lots of styling objects for each row of the list individually. This puts a lot of traffic on the JS->Native bridge. Try using stylesheet instead of passing the styles inline.
I strongly recommend everybody to read the article in the link attached below to optimize your flatlist.
https://reactnative.dev/docs/optimizing-flatlist-configuration
Try to set unique key for each item using keyExtractor
For Example:
render() {
return (
<List>
<FlatList
...
keyExtractor={item => item.email}
/>
</List>
);
}

Crop image with react-native

Hello World,
I trying to crop an image like explain on the React-native Doc
<Image source={{uri: this.props.image, crop: {left: 50, top: 50, width: 100, height: 100}}} style={{height: 100, width: 100}}/>
But it's doesn't work the image is not cropped.
Any idea?
From the docs:
On the infrastructure side, the reason is that it allows us to attach metadata to this object. For example if you are using require('./my-icon.png'), then we add information about its actual location and size (don't rely on this fact, it might change in the future!). This is also future proofing, for example we may want to support sprites at some point, instead of outputting {uri: ...}, we can output {uri: ..., crop: {left: 10, top: 50, width: 20, height: 40}} and transparently support spriting on all the existing call sites.
React Native Image is not currently supporting image cropping, at least not the way you pointed, however you still have other options that will do the same job.
ImageEditor:
React Native Component, again from the docs:
Crop the image specified by the URI param. If URI points to a remote
image, it will be downloaded automatically. If the image cannot be
loaded/downloaded, the failure callback will be called.
Cropping doesn't require linking.
Image Crop Picker another package that offers cropping, but in a different way: Picking. Requires linking, but thankfully it also supports RN versions > 0.40.
I haven't used any of them, but if I were you, I would first try Image Editor, since you don't need any additional installation except importing.
you can use package for cropping the image. first package for uploading image and second for cropping the image.
react-native-image-picker
react-native-image-crop-picker
for uploading image you can use first package function like this
const pickImage = () => {
launchImageLibrary({quality: 0.5}, response => {
if (!response.didCancel) {
setUri(response.assets[0]);
setCropImg('');
}
});
};
for cropping the image you can use second package function like this
const Crop_img = () => {
ImagePicker.openCropper({
path: uri.uri,
width: dimensions.width - 30,
height: dimensions.height / 3.5,
maxFiles: 1,
showCropFrame: false,
}).then(image => {
console.log(image.path);
setCropImg(image.path);
});
};

React native navigator animation not translating full device width

I have a React Native app running on Android with a Navigator component. I'm finding that when transitioning between scenes that the animation is only translating about 1/3 of the device width. I have seen that within the NavigatorSceneConfigs https://github.com/facebook/react-native/blob/v0.15.0/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js that the library is using the Dimensions of the device which is reporting 411.42857.... I'm running this on a Nexus 6 which is reporting a PixelRatio of 3.5. These numbers seem correct since multiplying these gives the devices resolution of 1440.
I have checked that the navigator and scene are filling the full device width. I would have expected it to translate the full width of the device. What have I missed configured or how have I missed understood what to expect from the Navigator animations?
class Root extends Component{
render() {
const routes = []
routes.push({
title: 'first'
})
routes.push({
title: 'second'
})
return (
<View style={{flex:1, backgroundColor: "transparent"}}>
<Navigator style={{flex:1, backgroundColor: 'red'}}
initialRouteStack={routes}
sceneStyle={{flex:1}}
renderScene={(route, navigator) =>
<View style={{flex: 1, backgroundColor:'transparent'}}>
<Text>{route.title}{Dimensions.get('window').width} {PixelRatio.get()}</Text>
</View>
}
/>
</View>
);
}
}
AppRegistry.registerComponent('Seed', () => Root);
This was a bug on ReactNative 0.14 and 0.15. I have justed test 0.16 and it is fixed.
https://github.com/facebook/react-native/issues/4221

Resources