React Native Redux performance issue app freeze - react-native

I'm building a React Native application (using Redux(Thunk)) where I lately have been facing performance issues that I haven't been able to find a good solution to.
The issue is that the UI/App is freezing/blocked every time I call an action, whether the state changes or not. The app is dealing with accounts data, and the issue started when the account count grew to about 3k accounts. I tested the following:
The time it takes to create the new state in the reducer.
For unnecessary renders.
Any manipulation on the data done in mapStateToProps or elsewhere.
None of the above is taking long or seems to be the issue. The example that I am testing on is a list view of the accounts, with an animated pop up for filtering. When I add a filter in the pop up and saves it (updates the store and closes pop up), I can see the accounts filtering instantly behind the pop up, but the UI/pop up animations is frozen for 2-3 seconds, before closing.
The data is stored with id's as index for accounts and a separate object for filters:
{
accountByIds: { acc1: {...}, acc2: {...} },
filters: {...}
}
I have been wondering whether using ImmutableJS would help, but as the application have grown rather large and I am not sure this would help, as the issue doesn't seem to be to many renders etc., I haven't changed it.
I am currently updating the store like below:
return {
...state,
filters: {
...state.filters,
newFilter
}
}
In sum up, it seems that when the store updates, the appropriate renders are called right away, but then the app seems to freeze up for a moment.
If anyone is able to shed any light on this or point me in the right direction I would appreciate it.

Related

Using State For React Native Navigation

I am fairly new to React Native and I am building an app that will be using navigation. I have a solution for navigating screens that seems to work just fine however I do not see many people using this approach. I am using state in a navigation component to render a specific screen(or component).
The main solution(the documented solution) I see is using this package
#react-navigation/native #react-navigation/native-stack
My current solution works like this:
const [screen, setScreen] = useState(0)
const returnScreen = () => {
switch (screen) {
case 0:
return <AccountScreen/>
}
}
When you click a menu icon, it would change the state to say 1, and render a different screen.
Can someone help me understand why this is a bad approach or an uncommon approach(if it is)? My app will only have 5-7 screens, I am mainly looking to understand why the library is the community approach and if I am missing something.
If you wanted the ability to navigate to other screens to exist outside your menu/drawer you will have to either prop drill your setScreen function or create a context to make it accessible to other components. While this isnt hard to do, it is something that all #react-navigation navigators do without requiring additional code.
If you wanted to enable going to the previous screen, your current navigation code becomes obsolete. Just returning a screen is no longer enough, as you need to track and modify navigation history. So you write more code that you have to test and debug (keep in mind that #react-navigation/stack already does this).
If you are certain that your app will remain small and your navigation features wont increase too much then your approach is completely fine. But if your app will increase in size then you probably will find yourself implementing, testing, and debugging features that have already been implemented, tested, and debugged by the #react-navigation community

Running cleanup when a react native app is closed

In my react-native app's code:
I am monitoring AppState changes
When AppState changes to 'inactive' or 'background', I write about 2000 key/value pairs of data to AsyncStorage
When the app starts I read this data
When I test the app on android (didn't test on iOS yet):
If I minimize the app, the data is written as expected. I can see that by closing the app after minimizing it, and restarting it
However, if I click on the 'Recent' button in the navigation bar (which changes AppState to 'inactive') and immediately click on the 'X' button at the top right of the app, the data doesn't seem to be written, or at least not all of it (I am not checking all 2000 values, just few of them). If I try to write only few values, they are written
Question:
The explanation to the above behavior seems to be simple: the app has enough time to perform few operations before I click on 'close', but not to write 2000 values. Is there a way to perform the writing before the app is closed?
Actually it's not recommended to do any async function or anything that could take too long. Because you don't have the control of how much time it will take to write the 2000 values. The app can go to background and the SO can kill the proccess of your app. When the app is in background is the SO that controls it.

React-native redux navigation state reset after crash

We are using redux navigation and persisting redux state. However, sometimes when the user navigates to the page and an unexpected error occurs app crashes with a white screen. If the app is launched again it obviously crashes again because the same state is loaded. As the result app becomes unusable - the white screen crash straight after the app launches.
I guess it is fine to crash the app if there is a bug on one of the screens, but because of one buggy screen I don't want to make the entire app unusable for a user and it would be great to somehow reset a state and enable the user to keep using other functionality of the app.
Any ideas on how to achieve that?
I think the real question is: do you want to persist navigation state at all? Do you really want to load last visited screen on app launch rather than the first screen of the app? I've never seen an app that does this
Another question is how do you persist your navigation state in redux. It was only a pattern when using older versions of react-navigation (v1 and v2 if I recall correctly) but even then it wasn't the optimal pattern to keep navigation state in redux (redux integration of react-navigation v1), let alone persist it on restarts. Navigation is something that should start fresh every time user launches the app
Also read about state persistence in current version (v5 - https://reactnavigation.org/docs/state-persistence) usually there is no redux involved at all
About state persistence: usually you only persist long-living things like auth state, authorization tokens, user settings, but not some dynamic data that gets discarded often. For example, if you open some page and fetch data to display on that page, there is no reason to persist that data in AsyncStorage, because why would you? This data should be reloaded every time page opens instead of restored from persisted state. Redux-persist lets you whitelist or blacklist different parts of the state
To summarize:
1. figure out if you need to persist navigation state at all. If not, problem solved
2. if you do, try to setup redux-persist the way that it doesn't persist short-living error-prone data
3. figure out how navigation state is persisted (through redux or on it's own, see examples in the links I provided earlier)
4. integrate react-native-exception-handler, catch exceptions and reset persisted navigation state in case of a crash

HighStock | Page crashing when panning

first of all, I'd like to point out that I already checked this SO Question and, sadly, it didn't solve my problem.
My issue is as follows:
I need to display charts with, sometimes, up to 5 times 100 000 points and for that I decided to use HighStock (it is not my only reason, but let's not get off topic already).
Everything works fine except for one (not so little) detail: if I try to click and drag on the chart with my mouse (for zooming or panning), the whole page crashes, or at least stops responding for 30+ seconds. And that is weird because I don't face this problem if I zoom/'pan' using the timeline below the chart.
Here is a JSFiddle showing what I mean (comment the line 40 in order to reproduce the issue).
chart: {
/* panning: false */
}
Because of that, I also tried using HighCharts and there I have no issue (other than the graph not looking as good... but that's because I almost didn't configure anything).
After doing some testing, I noticed that disabling the panning solves my problem.
chart: {
panning: false
}
But then, the only way to move/zoom the chart is by using the timeline which, even if it is not that big of a deal, can be annoying when using a phone (if you go in landscape mode in order to see more of the chart, because my website isn't just displaying a chart alone, it becomes kinda impractical).
tl;dr:
I have the solution but I'd like to know if it's a know issue, the solution, the reason for that behavior.
Should I mark my question as "I answer my own question" ?, in the end I'm still not sure why it it happening, all I know is a fix

Figuring out performance issues

For the last few days I've been fighting performance issues of FlatList inside application I've created.
FlatList consists of static header and x rows. In the measured case there are 62 rows, every one of them consists of 4 components - one of them is used 4 times, which sums up to 7 row cells. Every cell of this list is either TouchableNativeFeedback or TouchableOpacity (for testing purpose those have attached () => null to onPress. There is shouldComponentUpdate used on few levels (list container, row, single cell) and I think render performance is good enough for that kind of list.
For consistent measurements I've used initialNumToRender={data.length}, so whole list renders at once. List is rendered using button, data loading isn't part of my measurement - it's preloaded to component local state.
According to attached Chrome Performance Profile JS thread takes 1.33s to render component. I've used CPU slowdown x6 to simulate Android device more accurately.
However, list shows up on device at around 15s marker, so actual render from button press to list showing up takes more than 14s!
What I am trying to figure out is what happens between JS rendering component and component actually showing up on screen, because device is
unresponsive whole that time. Every touch event is registered, but it is played out only when the list is finally on screen.
I've attached trace from chrome dev tools, systrace taken using android systrace tool and screen from android profiler (sadly I couldn't find option to export the latter).
Tracing was run almost simultaneously - the order is systrace, android profiler, chrome dev tools.
What are the steps that I should make to help me understand what's going on while the app freezes?
Simple reproduction application (code in src.js, first commit)
Chrome Performance Profile
Systrace HTML
Android Profiler
Looking at the source code you posted, I don't think this is a React rendering problem.
The issue is that you're doing too much work in your render method and the helper methods you are calling during the render pass.
Every time you call .filter, .forEach or .map on an array, the entire list is iterated over n times. When you do this for m components, you get a computational complexity of O(n * m).
For example, this is the TransportPaymentsListItem render method:
/**
* Render table row
*/
render() {
const {
chargeMember,
obligatoryChargeNames,
sendPayment,
eventMainNavigation
} = this.props;
/**
* Filters obligatory and obligatory_paid_in_system charges for ChargeMember
*/
const obligatoryChargesWithSys = this.props.chargeMember.membership_charges.filter(
membershipCharge =>
membershipCharge.type === "obligatory" ||
membershipCharge.type === "obligatory_paid_in_system"
);
/**
* Filters obligatory charges for ChargeMember
*/
const obligatoryCharges = obligatoryChargesWithSys.filter(
membershipCharge => membershipCharge.type === "obligatory"
);
/**
* Filters obligatory adjustments for ChargeMember
*/
const obligatoryAdjustments = this.props.chargeMember.membership_charges.filter(
membershipCharge =>
membershipCharge.type === "optional" &&
membershipCharge.obligatory === true
);
/**
* Filters obligatory trainings for ChargeMember
*/
const obligatoryTrainings = this.props.chargeMember.membership_charges.filter(
membershipCharge =>
membershipCharge.type === "training" &&
membershipCharge.obligatory === true
);
/**
* Filters paid obligatory adjustments for ChargeMember
*/
const payedObligatoryTrainings = obligatoryTrainings.filter(
obligatoryTraining =>
obligatoryTraining.price.amount ===
obligatoryTraining.amount_paid.amount
);
// This one does a .forEach as well...
const sums = this.calculatedSums(obligatoryCharges);
// Actually render...
return (
In the sample code there are 11 such iterator calls. For the dataset of 62 rows you have used, the array iterators are called total of 4216 times! Even if every iterator is very simple comparison, simply iterating through all those lists is too slow and blocks the main JS thread.
To solve this, you should lift the state transformation higher up in the component chain, so that you can only do the iteration once and build up a view model that you can pass down the component tree and render declaratively without additional work.
I was trying different things for a while now, I was even considering wrapping native Android RecyclerView, but to be fair, it seemed quite a challenge as I've had no prior experience with native Android code.
One of the things I've tried over the past few days was using react-native-largelist, but it didn't deliver promised performance improvement. To be fair, it might've been even slower than FlatList, but I didn't take exact measurements.
After few days of googling, coding and profiling I've finally managed to get my hands on this Medium post, which references recyclerlistview package and it seems to provide better experience than FlatList. For profiled case rendering time dropped to about 2s, including 300ms of JS thread work.
It's mandatory to notice that improvement of initial rendering comes from reduced number of items rendered (11 in my case). FlatList with initialNumToRender={11} setting renders initially in about the same time.
In my case initial rendering, while still important, isn't the only thing that matters. FlatList performance drops for larger lists mainly because while scrolling it keeps all rendered rows in memory, while recyclerlistview recycles rendered rows putting in new data.
Reason of observer performance improvement for re-rendering is actually simple to test. I've added console.log to shouldComponentUpdate in my row component and counted how many rows is actually re-rendered. For my row height and test device resolution recyclerlistview re-renders only 17 rows, while FlatList triggers shouldComponentUpdate for every item in dataset. It's also worth noticing that number of rows re-rendered for recyclerlistview is not dependent on dataset size.
My conclusion is that FlatList performance may degrade even more with bigger datasets, while recyclerlistview speed should stay at similar level.
TouchableNativeFeedback inside recyclerlistview also seem to be more responsive, as the animation fires up without delay, but I can't explain that behaviour looking at profilers.
There's definitely still room for improvement in my row component, but for now I am happy with overall list rendering performance.
Simple reproduction application with recyclerlistview (code in src.js, second commit)
Chrome Performance Profile (recyclerlistview)
Systrace HTML (recyclerlistview)
Just give one more source to try.
You can enable Hermes in android/app/build.gradle like this.
project.ext.react = [
entryFile : "index.js",
enableHermes: true, // clean and rebuild if changing
]
My app not crashing but the touchable will stop working after some heavy process. When this happens, the process is still running ok, the app's drawer is still working. But all touchable in my app stop working without any error message. I spent 3 days to find a solution and finally tried hermes. On react native docs, it says
Hermes is an open-source JavaScript engine optimized for running React Native apps on Android.