When to use ResourceSavingView? - react-navigation

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.)

Related

Using React Hooks to Show Component Render Time and Date

My application contains a view that is a functional react component. I'm trying to add a timestamp at the bottom of the view displaying the date and time when the user navigated to this view. I'm wondering what would be appropriate way to implement this using hooks. I guess one option would be to use const [date] = useState(new Date()) and simply provide no setter for the state since it would never change. However, this makes me wonder if useState is the appropriate hook to begin with. Should I just do const date = new Date() in the component body or would that have some unexpected side effects?
For what you intend to accomplish, the code below would work just fine:
// solution # 1
function MyComponent() {
const [time, setTime] = useState(new Date());
return (
<div>
View created at {time.toLocaleTimeString()}
</div>
)
}
However, it is important to know that it is not following React rules. That is because we are calling new Date() from inside the component, making it impure. React expects the components to be pure functions. This means that it should return the same thing for the same input. But React cannot guarantee that a function component doesn't have side effects, and that is why that first solution would work just fine.
According to this gist, a better approach for this task would be:
// solution # 2
function MyComponent() {
const [time, setTime] = useState<Date>();
useEffect(() => {
setTime(new Date());
}}, [])
return (
<div>
View created at {time && time.toLocaleTimeString()}
</div>
)
}
Roughly speaking, all side effects should live inside useEffect. So, things like calling an external API or calling impure functions (such as Math.random() or Date.now()) should be there. From my understanding, this solution would be more appropriate.
If we are not obeying React guidelines, the library might not work as advertised. Before version 18, React had only synchronous rendering. This means that the moment the render phase is kicked off (either by a initial render or by a state update), nothing could interrupt it from committing those updates to the browser. In that situation, I don't think the first solution I presented would be problematic. But, in React 18, we have the concurrent mode features by which rendering can be interrupted before the diffs are committed to the screen. With that, React can be more intelligent about how it breaks up all the work it needs to do (for example, we can now define high-priority updates).
In order for these new APIs to work properly, React assumes that the components are pure functions, which means that it has no side effects. These APIs are very new and there are still many more additional features coming in. I am not quite sure about how sensitive they are to function impurity, but I believe our second solution (with all side effects living inside the useEffect) is a better bet for this task.
The following seems to work. Still not sure if this is optimal though.
import React from 'react'
export function View(_props) {
const [viewCreated] = React.useState(Date.now())
return (
<div>
View created {String(new Date(viewCreated))}
</div>
)
}
useState(new Date()) is better. Here is complete component
import React, { useState , useEffect } from 'react'
export const DateTime = () => {
var [date,setDate] = useState(new Date());
useEffect(() => {
var timer = setInterval(()=>setDate(new Date()), 1000 )
return function cleanup() {
clearInterval(timer)
}
});
return(
<div>
<p> Time : {date.toLocaleTimeString()}</p>
<p> Date : {date.toLocaleDateString()}</p>
</div>
)
}
export default DateTime

What is the most performant way for dynamic styling in React-Native?

In React-Native, you can use Stylesheet to create css-like stylesheets. The main reason of using styleshee.create in favor of plain js-objects is increased performance. However, you often might want to style components dynamically, often based on their props. I basically found three approaches of doing this:
Note for the following examples: Consider const styles ... to be declared outside of the Component, as it's a common pattern and you might want to share styles between different Components. Consider everything below the tree dots as part of the render function.
Using an array of styles:
const styles = StyleSheet.create({viewStyle: {backgroundColor:'red'}})
...
return <View style={[styles.viewStyle, {color: this.props.color}]} />
Using Stylesheet.flatten:
const styles = StyleSheet.create({viewStyle: {backgroundColor:'red'}})
...
const flattenedStyle = StyleSheet.flatten(styles.viewStyle, {{color: this.props.color}})
return <View style={flattenedStyle} />
Using a function to create the stylesheet:
const styles = (color) => StyleSheet.create({
viewStyle: {
backgroundColor:'red',
color: color
}
})
...
const style = styles(this.props.color).viewStyle
return <View style={style} />
I am wondering which approach is the best regarding to performance, or if there even is another, more performant way? I think Option 2 and 3 are no way to go at all, because dynamically creating new stylesheets on prop-changes undermines the whole purpose of stylesheets. I am happy for any thought or hints on this subject!
Here you can do dynamic styling in react native for each styling.
Like this
<Text style={styles.simpleText('red')}>Required field</Text>
// In styling
const styles = StyleSheet.create({
simpleText: (colorProp = 'black') => ({ // default black set
fontSize: 14,
color: colorProp,
})
})
and you can also pass any data type for conditional styling
One of the approach
// homeSreen
<View style={styles.filterButton(isSelected)}>
<Text> Strawberry </Text>
</View>
// styles.js
import { StyleSheet } from 'react-native';
import { Colors } from '../../theme';
export default StyleSheet.create({
container: {
backgroundColor: Colors.lighter,
},
filterButton: isSelected => ({
padding: 15,
backgroundColor: isSelected? Colors.background.primary: Colors.background.secondary
}),
});
You could memoize stylesheet creation using React hooks, but first you need to do some performance checking in order to determine if stylesheet creation is in fact a CPU and/or memory hog worth optimizing.
Here's an example:
const styles = (color) => StyleSheet.create({
viewStyle: {
backgroundColor:'red',
color: color
}
})
/*
even though makeStyle is defined in EVERY render,
React will only run it ONCE for any given props.color distinct value.
The resulting value `styles` survives re-renders
*/
const makeStyle = () => styles(props.color)
const styles = useMemo(makeStyle, [props.color]);
And here's the official documentation.
Did you consider CSS in JS libraries like Styled components?
You can pass props and get dynamic style regard that:
https://styled-components.com/docs/basics#passed-props
Possibly a bit overkill for simple dynamic styling but Reanimated is very performant and will run the style transition at 60fps https://github.com/software-mansion/react-native-reanimated
It archives this by declaring all the styles needed for an animation/transition ahead of time and runs them on the native thread so there is minimal communication across the JS->Native code bridge.
There is a better explanation on their about page here https://docs.swmansion.com/react-native-reanimated/docs/about

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>
);
}

How to make the images a button?

I am working on a react native app where I am trying to make some images act like a button so that when you press on them they print a statement in the console.
The images are displayed like this:
The code I have is:
class ImageList extends Component {
componentWillMount(){
const ds = new ListView.DataSource({
rowHasChanged: (r1, r2) => r1 !== r2
});
this.dataSource = ds.cloneWithRows(this.props.images);
}
imageTouched(){
console.log('pressed');
}
renderRow(rowData){
const {uri} = rowData;
return(
<View style={{padding: 1, alignSelf: 'flex-start'}}>
<TouchableHighlight onPress={this.imageTouched}>
<Image style={styles.imageSize} source={{uri: uri}} />
</TouchableHighlight>
</View>
)
}
render() {
return (
<View>
<ListView
contentContainerStyle={styles.list}
dataSource={this.dataSource}
renderRow={this.renderRow}
/>
</View>
);
}
}
var styles = StyleSheet.create({
imageSize: {
//newWidth is the width of the device divided by 4.
//This is so that four images will display in each row.
width: newWidth,
height: newWidth,
padding: 2
},
list: {
flexDirection: 'row',
flexWrap: 'wrap'
}
});
When I run it there are no errors but when I touch the images nothing happens. I have checked the console but nothing is printed.
How do I get each image to act as a button?
Like others have mentioned, the problem is that this is not bound in the renderRow()-method. I think the easiest way to fix this is to change renderRow() to be an arrow-function:
renderRow = (rowData) => {
const {uri} = rowData;
return (
<View style={{padding: 1, alignSelf: 'flex-start'}}>
<TouchableHighlight onPress={this.imageTouched}>
<Image style={styles.imageSize} source={{uri: uri}} />
</TouchableHighlight>
</View>
);
}
Arrow function always have this set to their containing scope when invoked, so now this.imageTouched will resolve.
Notice that you don't have to do anything with your imageTouched()-function or invocation, since it's not referencing this.
PS. This syntax depends on Public Class Fields, which is a Stage 2 proposal of the language standard at the time of writing (likely to be included, already in use in internal React-code). This feature is possible to use with a babel-plugin that is enabled by default in React Native projects.
PS2. Note that declaring the method with an arrow function instead of using an arrow function in the invocation will create one instance of the method per component instance, instead of one instance per render. This should really be fine performance-wise.
React components defined as ES6 classes do not autobind methods' this context to the component instance. In this particular case, the renderRow callback is not bound and the context refers to the global application context instead, so the reference to this.imageTouched is undefined.
A common pattern you see a lot is to bind the callbacks in your render method:
<ListView
renderRow={this.renderRow.bind(this)}
/>
This, however, has the effect of creating a new instance of the function on every render, which causes unnecessary work for the garbage collector.
Another alternative is to use (lexically scoped) arrow functions and call the methods explicitly:
<ListView
renderRow={(data) => this.renderRow(data);
/>
But this has the same unwanted effect of unnecessarily creating functions on every render.
A slightly more verbose, but more "correct" way is to bind the callbacks in the class constructor:
constructor(props) {
super(props);
this.renderRow = this.renderRow.bind(this);
}
Try this
<TouchableHighlight onPress={() => this.imageTouched()}>
Make sure to bind renderRow
renderRow={this.renderRow.bind(this)}

How do I implement a common functionality across multiple views using backbone.js

Hello I'm trying to implement a custom functionality across multiple views using backbone.js. For example I need to have all the input textboxes in the application to change their visual appearance when they receive focus.
I am thinking to inherit a window.BaseView from the Backbone.View.extend and then to have all my views to extend the BaseView.
Could you please tell me if I'm on the right direction? Do you have other suggestion? Have you implemented something similar ?
I would put the common functionality in a base prototype and have all the views extend it or I would create a mixin object that has the functionality and extend the view with the mixin.
I didn't use backbone for similar task since different views have different elements and binding same focus function more than once is also wasteful. What I would do is either:
//If document is the container for your application, else could be #myapplication and so on.
jQuery( document ).delegate( 'input[type="text"]', "focusin focusout",
function(e){
if( e.type == "focusin" ) {
jQuery( this ).addClass("textbox-focused");
}
else {
jQuery( this ).removeClass( "textbox-focused" );
}
}
);
or CSS (not sure about browser support):
input[type="text"]:focus {
background-color: blue;
}

Resources