react native image loading is too slow - image

i get the 22 images from the react-native-image-filter.
this image are base64 format,
Here is the code on render runction.
render() {
const { images, imageIndex } = this.state;
return (
<View style={styles.container}>
{images?.length > 21 && <View style={{ flex: 1 }}>
<Image resizeMode={'contain'} source={{ uri: `data:image/png;base64,${images[imageIndex]?.base64}` }} style={{ width: 400, flex: 1 }} />
</View>}
<View style={{ height: 140, paddingVertical: 20 }}>
<ScrollView horizontal>
{images?.map((item, index) => {
return (
<View style={[{ marginHorizontal: 10 }, styles.center]}>
<Image
resizeMode={'cover'}
key={index.toString()}
onPress={() => this.setState({ imageIndex: index })}
source={{ uri: `data:image/png;base64,${item.base64}` }}
style={{ width: 80, height: 100, borderRadius: 8 }} />
</View>
)
})}
</ScrollView>
</View>
</View>
);
}
everything is good, but the image loading is too slow.
How can i increate the loading speed? Help me...

The problem is most definitely in Base64-encoded images.
The slowdown is because React Native has to pass quite long Base64 strings back and forth through the Native<->JS bridge, plus native platform has to parse Base64 and convert it into the correct memory representation for the native image view. Many optimizations provided by the platform are therefore missed.
Ideally, all image manipulations should happen on the native platform, and none of the actual image data should even touch JS.
I'd advise to try these two libraries for more performant filter implementations:
https://github.com/GregoryNative/react-native-gl-image-filters
https://github.com/iyegoroff/react-native-image-filter-kit

Related

Image disappears on component re-renders

I have a functional component that shows a preview of the image or video being uploaded. I render this conditionally based off a state variable called media:
const renderMediaInPreview = useCallback(() => {
if(media && media.type === "image") {
return (
<Image style={{ height: '100%', aspectRatio: 1, resizeMode: 'cover'}} source={{ uri: media.uri }} />
)
} else if(media && media.type === 'video') {
return (
<Video
style={{ flex: 1 }}
source={{ uri: media.uri }}
isLooping
shouldPlay
resizeMode={Video.RESIZE_MODE_COVER}
isMuted={true}
/>
)
}
}, [media])
I've wrapped it in a useCallback so the function is only called when the media value changes. However using console logs I am able tto see that whenever the other state of the component is changed this re-runs and even though the media value stays the same it make the image disappear. Any insights? Below is my return and clip of behavior.
return (
<SafeAreaView style={{ flex: 1 }}>
<KeyboardAwareScrollView
style={{ flex: 1 }}
resetScrollToCoords={{ x: 0, y: 0 }}
contentContainerStyle={{ flex: 1, alignItems: 'center' }}
scrollEnabled={true}
keyboardShouldPersistTaps={'handled'}
behavior={Platform.OS == "ios" ? "padding" : "height"}
removeClippedSubviews={true}
>
{ renderErrorMessage() }
...
</KeyboardAwareScrollView>
</SafeAreaView>
);
https://drive.google.com/file/d/1-fYSOYTP0W86rRaS_CHqWw_gYfZFBcXT/view?usp=sharing
Was able to track this down and figured out it was not in relation to an issue with useCallback. I had done some refactoring of code and media was changing slightly that I didn't notice because of the refactor and no longer rendering anything because if didn't match the above if/then check.
Minor error that ended up taking a ton of unnecessary time... sigh.

react-navigation BottomTabNavigator VERY slow on some Android devices

I've got a minimal test ReactNative test in Expo running. 2 screens, both just have Text within a View.
This is run on a MBP, and tested on my Android device (Note10+) via QR code.
It is not just slow, it's unresponsive most of the time, when it does navigate to a tab on click then it's either instant (rarely) or takes 5-10 seconds. But usually nothing happens.
There are no errors being shown, replacing the same code with a Stack or Drawer works fine.
2 basic screens:
function StackScreenA0({navigation}) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Test stack 0</Text>
<Button title="goto 1" onPress={() => navigation.navigate('Screen A1')} />
</View>
);
}
function StackScreenA1({navigation}) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Test stack 0</Text>
<Button title="goto 0" onPress={() => navigation.navigate('Screen A0')} />
</View>
);
}
App code (minus imports):
const Tab = createBottomTabNavigator();
function App() {
return (
<NavigationContainer>
<Tab.Navigator initialRoutName='Screen A0'>
<Tab.Screen name="Screen A0" component={StackScreenA0} />
<Tab.Screen name="Screen A1" component={StackScreenA1} />
</Tab.Navigator>
</NavigationContainer>
);
}
export default App;
I've never used Expo before but I can't imagine this is normal behaviour but cannot see a reason for it working so badly.
EDIT: I've found other devices to test and tabs are fine on iPad4, Galaxy9, but seriously bad on Note10+.
Rich

React Native: optimizing list of items

I have a list of items, each having a body and a source. Currently it renders like this:
const ListItem = (props) => {
const {body, source} = props.context;
return (
<View style={styles.item}>>
<View style={{backgroundColor: 'lightblue'}}>
<Text style={styles.body}>{body}</Text>
</View>
<View style={{backgroundColor: 'lightyellow'}}>
<Text style={styles.source}>{source}</Text>
</View>
</View>
);
}
That's a lot of nesting and containers. Could it be done more optimally?
I am going to post my comment as an answer.
Previous comment:
I guess it depends on your design, AFAIK this is fine within React Native assuming you're using an optimized way of rendering your list (e.g. using a FlatList or similar)
As per your following comment, I don't think it's monstrous at all.
Here's an alternative. However, for readability I would much prefer the snippet you posted in your question.
const ListItem = props => {
const items = [
{ key: 'body', backgroundColor: 'lightblue' },
{ key: 'source', backgroundColor: 'lightyellow' }
];
return (
<View style={styles.item}>
{
items.map(({ key, backgroundColor }) =>
<View style={{ backgroundColor }}>
<Text style={styles[key]}>
{ props[key] }
</Text>
</View>
)
}
</View>
)
}
The Text component does take backgroundColor so you can let go of the two Views:
<View style={styles.item}>
<Text style={[styles.body, {backgroundColor: '...'}]}>{body}</Text>
<Text style={[styles.source, {backgroundColor: '...'}]}>{source}</Text>
</View>
Additionally, I don't know what the styles.item consists of, but if you want to go all the way, you might replace the other container View with React.Fragment.

React native Image in two scrollView not showing

I started react-native and I am having a hard time finding how I can load a picture from an uri and show it into two scrollView (seems I need that, one for vertical and one for horizontal scrolls). I could make it works for a local file, but as soon as I get it from internet, the picture never show up except if I give a specific size.
<View style={{ flex: 1 }}>
<ScrollView contentContainerStyle={{ flex: 0, alignContent: "stretch" }}>
<ScrollView horizontal={true} contentContainerStyle={{ flex: 0, alignContent: "stretch" }}>
<Image
style={{ flex: 1, backgroundColor: 'white' }}
resizeMode='cover'
source={{ uri: 'https://cdn.pixabay.com/photo/2017/02/15/11/15/wintry-2068298_960_720.jpg' }}
/>
</ScrollView>
</ScrollView>
</View>
The same code without the scrollViews shows the image perfectly but with them the image never shows. I tried, the alignSelf:'stretch', or to set top/bottom/right/left to 0 and also width:'100%' and height:'100%' for the image nothing giving the expected result which is take all the space possible showing the image and be able to scroll inside it horizontally and vertically
Thanks for your help.
Finally found the answer :
var myUri = 'https://cdn.pixabay.com/photo/2017/02/15/11/15/wintry-2068298_960_720.jpg';
var mode = 'cover'
Image.getSize(myUri, (width, height) => { this.setState({ width, height }) });
return (
<ScrollView contentContainerStyle={{ flex: 0 }}>
<ScrollView horizontal={true} contentContainerStyle={{ flex: 0 }}>
<Image
style={{ width: width, height: height }}
resizeMode={mode}
source={{ uri: myUri }}
/>
</ScrollView>
</ScrollView>
);

ReactNative ListView inconsistent separator lines

On Android 4.4, ListView separator lines are inconsistent in thickness, and some do not render.
I can't see how this can be a code issue, this is how I render them:
separator: {
height: 1,
backgroundColor: 'grey',
}
...
<ListView
renderSeparator={(sectionID, rowID) =>
<View key={`${sectionID}-${rowID}`} style={styles.separator} />
}
.../>
Here is a screenshot of a View with this problem:
This issue does not happen on iOS or Android 6.
Anyone had this problem before?
Update
I did a test, this is not Android4 issue. It happens on all API version when running on Nexus One device (in android emulator)
I had this issue on iOS and worked around it by adding a hairline margin, like so:
<View
style={{
...styles,
borderWidth: StyleSheet.hairlineWidth,
margin: StyleSheet.hairlineWidth,
}}
>
{// ...row content}
</View>
Just give the height:hairlineWidth in style
I had the same issue and solved changing the view height from a number to StyleSheet.hairlineWidth as some folks said before. Trying to be more visual/specific:
Before:
renderItemSeparator() {
return (
<View style={{ height: .2, backgroundColor: 'rgba(0,0,0,0.3)' }} />
);
}
After:
renderItemSeparator() {
return (
<View style={{ height: StyleSheet.hairlineWidth, backgroundColor: 'rgba(0,0,0,0.3)' }} />
);
}
Actually there is no fix. It's RN "render-canvas-bug".
But I found hack solution.
<ListView
style={Style.listView}
dataSource={data}
renderRow={(data) => this._renderRow(data)}
/>`
Style.listView: {
backgroundColor: '#fff',
}, // or another backgroundColor you need
Then:
_renderRow(goods) {
return (
<View key={'goods_' + goods.id} style={Style.listView_item}>
<TouchableOpacity or View or ...
style={[Style.flex, Style.flexRow, Style.separatorRow, Style.u_paddingVerticalS, Style.u_middle]}
onPress={() => this._xyz(goods)}>
<View>
<AppFont>{goods.name}</AppFont>
</View>
</TouchableOpacity or View or ...>
</View>
);
}
Only important TouchableOpacity style is Style.separatorRow to render your separator. This style should be inside listView_item, where you can use another styles.
listView: {
backgroundColor: '#fff',
},
listView_item: {
paddingHorizontal: em(1.5),
},
flex: {
flex: 1,
},
flexRow: {
flexDirection: 'row',
},
separatorRow: {
marginBottom: 1,
borderBottomWidth: 1,
borderBottomColor: Colors.canvasColor,
},
You can use StyleSheet.hairlineWidth instead of 1 but it's not a must.
I reported it on GitHub
My workaround was to style the containing view and text like this:
const styles = StyleSheet.create({
rowViewContainer: {
flex: 1,
paddingRight: 15,
paddingTop: 13,
paddingBottom: 13,
borderBottomWidth: 0.5,
borderColor: '#c9c9c9',
flexDirection: 'row',
alignItems: 'center',
},
rowText: {
marginLeft: 15,
},
});
This is the ListView:
<ListView
dataSource={this.state.dataSource}
renderRow={(data) => <View style={styles.rowViewContainer}>
<Text style={styles.rowText}>
{data.bb_first_name}
</Text>
</View>}
/>
Looks nice:
This happens because you have empty rows in your data source. You can style your separators to see it
To avoid this just filter your data.
I faced the same issue when trying to render a Divider with a width of 0.5.
It rendered properly on devices with pixel ratio of 2 (e.g. iPhone SE 2nd gen.) but rendered random width on devices with pixel ratio of 3 (e.g. iPhone 12).
As suggested by other answers, using Stylesheet.hairlineWidth fixes the random width issue but the problem was that the width was thinner than 0.5 on devices with pixel ratio of 3.
So this fixed my problem:
import { PixelRatio, View } from 'react-native';
...
export const Divider = () => {
const width = PixelRatio.roundToNearestPixel(0.5);
...
return <View style={{ width }} ... />
}

Resources