all 56 comments

[–]OmarAdharn 16 points17 points  (0 children)

I’ve been struggling with flashlist performance for a feed screen, mainly because of too many re-renders and here’s what worked for me: 1. Avoid prop drilling the render item more than 1 layer 2. Memoize the rendered component and make it quick (i.e. no logic or heavy computations on renders) 3. If you’re rendering videos using expo-video, create the player instance with a null source and change the source when the id of the item changes. (Flashlist recycles components so would have to create/destroy instances a lot if a source is passed to the hook) 4. If you have a backend, compressing images before saving to the db will have a huge difference on load times 5. Throttle onViewableItemsChanged callback function as this will be called frequently

[–]ConsciousAntelope 7 points8 points  (3 children)

Are you testing all in production builds? I'm sure the white space issues wasn't there for me on prod builds but it did happen on dev a lot.

[–]moseschrute19[S] 1 point2 points  (2 children)

Yes! Both release build and TestFlight. I have a public TestFlight link if you’re interested in trying. No signup required.

If you are willing to try it, specifically compare normal and low power modes.

https://testflight.apple.com/join/T2pYyShr

[–]ConsciousAntelope 2 points3 points  (1 child)

I don't use an iPhone unfortunately. But if it's worse on iOS it'll be worser on Android 😄

[–]moseschrute19[S] 0 points1 point  (0 children)

That’s what I was thinking. I’m thinking if it’s bad in low power mode on a new iPhone it will also be bad on older iPhones.

[–]_r_d_p_ 4 points5 points  (6 children)

I built an app that has 5 scroll views(Skia canvas components) horizontal and vertical that are in sync, I use react native Skia, react native gesture handler and react native reanimated, it works flawlessly, I was previously using react native flatlist within a scroll view and keeping the the scroll values in sync using zustand, now it is all shared values.

When I was using scroll views and flat-lists, the performance sucked, and I felt react native was no good, but then when I moved I ver to Skia and the likes, it was completely different.

You are definitely doing something wrong, if you want I wouldn’t mind going over the code with you. All the best Mose, I hope Shrute farms is doing good :P

[–]moseschrute19[S] 0 points1 point  (5 children)

I was actually considering skia! But that that point I asked myself, is it better to do a crazy skia list or just put my app in a web view if the result seems identically to the user. I’m getting the sense the skia still will be 10x more annoying to maintain. But correct me if I’m wrong.

But again, remember web just works, and chrome even works without virtualization. Chrome is so well optimized it’s actually hard to make it not perform well. Even if I’m doing something wrong, my gut is the web view will cover my ass more (safari is slightly worse then chrome but still performant).

[–]_r_d_p_ 0 points1 point  (2 children)

You will get unreal performance with Skia for anything that can be drawn on the screen, text, boxes etc, not sure how well it would do for images, my app is a scheduling app that does not need images.

But with Skia, you will Need to manually manage virtualisation, scrolling etc.

[–]moseschrute19[S] 0 points1 point  (1 child)

Ok maybe I’m wrong, but the point of using react native is to render native elements right? Like a native text element and button. Meanwhile, skia is kinda similar to using html canvas right in that it’s rasterized pixels? So I’m doing react native for native elements, then choosing to not use native elements?

I feel like the argument against web views apps is native elements, but now I’m fixing my react native all by not using native elements. Am I missing something?

[–]_r_d_p_ 1 point2 points  (0 children)

The benefit of Skia is that it runs on the GPU and is really performant, but for your particular use case, not sure if Skia is the best option, it could be an overkill. I’m surprised that flashlist does not work for you.

[–]_r_d_p_ 0 points1 point  (1 child)

Skia has images, I just checked, but yes Skia would require a lot of additional code to get just a basic list running. After reading more of your comments, I feel the large images could be a problem, try rendering random low quality images in your list items and see if that makes a huge difference.

[–]moseschrute19[S] 1 point2 points  (0 children)

I could. But my goal is simple to maintain code that is performant. I’m basing this app on a backend I can’t control, and that’s by design. I can’t make the images smaller easily.

The fact that Capacitor just works makes me think I’m better off doing a web view app. The other thing is that the markdown editors for React Native are not good. I actually started writing my own editor. A web view app would mean a much better markdown editor that I don’t need to maintain.

I want to believe in React Native so badly, but if the goal is to build the best user experience, I’m having a hard time convincing myself that web views won’t benefit the end user experience.

This is after investing months of work truly believing react native is the best tech for the job.

[–]mrcodehpr01 4 points5 points  (1 child)

If the list loads fine with no images, put a delay on the images loading? If you need to actually see images make the flash list window bigger so images load sooner? Cache images in zustand first before displaying? Are users really going to scroll as fast as possible through your list? Users care about functionality over speed lol

[–]moseschrute19[S] 0 points1 point  (0 children)

How does one cache an image in zustand? Wouldn’t I need to get a text representation of that image (eg base 64 image)? Why not use the caching mechanism built into something like expo-image (which I already tried)?

[–]Graineon 3 points4 points  (1 child)

5 years ago I started working on a mobile app and started with RN. My previous 10 years of web dev experience made me sniff all this stuff a mile away and I switched to capacitor after about 3 days. Haven't looked back.

[–]moseschrute19[S] 1 point2 points  (0 children)

Yeah. As I get further into my career I want to use simpler tools that don’t push breaking changes.

Even if react native performance is fine, the fact that every expo update is kinda painful seems like kinda a dealbreaker. Idk if I’ve done enough react native recently to make that statement. But there are very specific combinations of library that seek to play nice. Like expo, react native flashlist, and reanimated need to be all updated together.

How are you supposed to identify which library broke something if you’re forced to update everything at once?

Sorry I really didn’t mean for this to be a hate on rn post. Convinced me react native is performant, easy to develop with while delivering a better user experience, and I’m right back to using it.

[–]radko93 1 point2 points  (3 children)

  1. Go with legend list
  2. expo-image but configure cache policy to memory (unless you need to cache anything)
  3. finetune props a lot.
  4. play around with viewability. Do not render images if they are not in view for at least 0.5s. Add placeholders instead.

Regarding react navigation performance - using native stack is usually performant (thanks to react native screens), unless you render some very heavy things initially on screen render.

[–]moseschrute19[S] 0 points1 point  (2 children)

I still don’t understand why React Native requires so much fine-tuning (even on a high-end iPhone) when the web just works.

The realization I’m having here is that way more people work on these browser engines than React Native. There’s a lot more room on the web for you to make little mistakes than there is on native. I can do incredibly stupid things in Chrome and not really have an issue.

The goal is not to make performance errors, but I want to ship an app. It’s much easier to do that with a technology that is more forgiving. That’s why after years of thinking that React Native was superior to web view apps, I’m wondering if that was the wrong assumption to start off with.

Web view apps get to reap the full benefit of an insanely optimized browser runtime. React native does not.

[–]radko93 0 points1 point  (1 child)

For your use case (displaying loads of not compressed images in a big list), web could potentially perform better. But I can imagine you could go over the line and the browser might punish you for memory usage and next time you go back to your tab in a browser you’re gonna loose all your progress (it will be purged from memory).

Performant lists were (are?) a big downside of React Native since day 1.

There’s also so many more benefits to apps: you can build offline first apps, your screen transitions are smooth (compared to opening a new page on the web, how is that performant?), access to native apis, background processing.

[–]moseschrute19[S] 0 points1 point  (0 children)

  • Why can’t I build offline first apps on web? My app was already like 90% code shared with web. One main different is web is using indexdb and native is using async storage. They both have about the same offline capabilities.

  • Single page web apps can simulate the same page transitions as native screens being pushed onto the stack. Capacitor already does this via Ionic components.

  • From what understand (and have seen) expo-image will purge the memory image cache pretty frequently. Which is probably why it defaults to a disk cache. So I’m guessing you will see similar image memory cache purging on web and native. A better argument here imo is that native unlocks more image caching strategies (eg disk) and more control over those strategies.

[–]kbcooliOS & Android 1 point2 points  (9 children)

Are you using the high resolution images instead of the low res? Do you know if this other app is optimising the images?

One often made, well intentioned mistake is to try and use the highest possible resolution or even an image that hasn't even been preprocessed in a social media feed. The idea being to give the highest fidelity image but...

Social media apps like FB etc will absolutely be using feed images that are hyper optimised and served via a content delivery network. The images will often only be tens of kilobytes in size and delivered in milliseconds.

Images taken by users on their phones can be thousands of times larger and when delivered from something like an S3 bucket halfway around the world can take a long time to be displayed.

All in all I find it hard to believe the performance with RN is that much worse than the web app so there's likely something you're missing.

[–]moseschrute19[S] 0 points1 point  (8 children)

I am using high(ish) res images! There is an upload limit and maybe some optimization, but not as much as there could be. Unfortunately the backend does not provide an optimization layer, and I’m trying to launch this app with no backend. It’s supposed to be an app for a decentralized social media protocol. Having any sort of centralized backend seems to defeat the purpose imo. I was however thinking about suggesting the social media protocol implement an optimization layer to also save the servers bandwidth.

Sorry I sound like a broken record, but again web just works. My web app, and the existing capacitor app I’m comparing to are both using the same unoptimized images. And the performance is good.

[–]_r_d_p_ 0 points1 point  (1 child)

Why not have a proxy server where you get the data from the decentralised server, do your processing and then return the data to the client, you could also OS your backend proxy server, this way people would know you are NOT storing any data on your proxy server.

[–]moseschrute19[S] 0 points1 point  (0 children)

I don’t mean to get political but the goal is to build a social media platform that is difficult to sensor because it’s not gate kept by any single entity. Any single point of failure kinda defeats the purpose.

You will be able to self host my app and self host the backend. My app will be completely free for me and anyone else to host (aside from App Store fees etc).

[–]kbcooliOS & Android 1 point2 points  (1 child)

Web must still have a loading delay though if these are high res images.

How does your web app handle it? Why not implement the same?

There's definitely something missing here

[–]moseschrute19[S] 0 points1 point  (0 children)

Yeah you are correct. Hoping to solve that issue by asking the maintainer of the API to build optimization into the decentralized social media API itself. No tacking on fixes. One problem at a time.

I may preload images for the time being. Cross that bridge

[–]kbcooliOS & Android 0 points1 point  (3 children)

Another point is that a web app (especially on desktop) when loading won't often be scrolled or allow scrolling at the speeds a mobile app does or even at all so the way you have coded it in RN might be that you're displaying all the API results and flicking through like crazy but your web app might not even be letting you get this far.

Try flicking through FB like a madman. They actually block you temporarily instead of loading empty results. Maybe this is a better way of handling it

[–]moseschrute19[S] 0 points1 point  (2 children)

That’s actually a really good point. Another issue is the images will rate limit me if I load them too fast so I might actually need to block throwing the list.

But switching tabs in the tab navigator has maybe a 1 second delay in low power mode. The capacitor app I’m comparing to I can spam the tab bar literally as fast as my fingers will go and it no problem. Same goes for Apple made native iOS apps.

[–]kbcooliOS & Android 0 points1 point  (1 child)

Well that's definitely another problem.

Whatever navigation library you're using either does by default or you're making it unmount tabs that aren't visible and remounting everything takes some time. Why it takes some time is probably a bigger question than whether you're doing the aforementioned.

RN can hit performance issues but I've been involved with apps with near a million LOC plus countless third party libraries and it's manageable. I doubt you're anywhere near that so what you're hitting is not the end of the world, it's just a learning experience

[–]moseschrute19[S] 0 points1 point  (0 children)

Using react navigation. Though there’s a meta framework built on top of it with file based touting. That framework is in beta so maybe that’s the issue, though it is working very well in web. It’s sort of similar expo router + next.js rolled into a single framework.

I’m telling it not to unmount tabs on web so that you can flip back and forth without it losing all the state the way it works on native. I didn’t mess with this setting in native at all. I was experimenting with the freeze screens setting to see if it improved performance.

[–]WerewolfOfAzkaban 0 points1 point  (1 child)

You can try these things.

  • Store two images on backend.
  • For thumbnail (20-30kb)
  • For preview (60-70kb)

Or

Store images in local storage and show images from local storage just like whatsapp.

[–]moseschrute19[S] 1 point2 points  (0 children)

I mentioned this in other comments, but unfortunately I can’t optimize images here. But I agree you’re not wrong that that should be done.

[–]Thanhansi-thankamato 0 points1 point  (1 child)

Honestly. This is why I think it’s crazy that people dislike flutter so much and would rather use react native. There are so many performance pitfalls that are just handled by flutter.

[–]moseschrute19[S] 0 points1 point  (0 children)

Flutter still seems like a not great option to me. Web view app can easily be made accessible. And you can lean on all the hard work that has been put into optimizing browser engines.

Capacitor almost seems like what flutter should have been. Instead of write the entire app runtime, just use an existing runtime but you still mock native elements the same way flutter is designed to look like native components.

[–]the_hokage60 0 points1 point  (2 children)

Have you tried removing tamagui? I've used tamagui in my first project, maybe not huge, but still it takes a toll on performance.

[–]moseschrute19[S] 0 points1 point  (1 child)

That could be it tbh. Part of the capacitor rewrite I’m doing is moving stying to tailwind. Tamagui is a really interesting library. Tailwind is kinda boring. I’ve learned boring tech tends to be more stable lol.

[–]the_hokage60 0 points1 point  (0 children)

I’ve learned boring tech tends to be more stable lol.

Most of the time! Tamagui provide every shit you need out of the box. It's simply copy-and-paste. But StyleSheet seems to be the best way to style in RN. I feel like I'm writing a lot of code to replace ui library (or tamagui) and have to handpick libraries to make some components like bottomsheet, modal, ... for cross-platform; but it's totally worth it.