How Qunar Optimized React Native ListView Performance Across Platforms
This article details Qunar's experience optimizing React Native ListView performance, covering RN's internal implementation, identified bottlenecks, and two practical solutions—JavaScript‑based cell reuse and a native UITableView bridge—while also comparing RN with Weex and discussing cross‑platform considerations.
Introduction
Earlier, the author attended a T‑Salon in Beijing where mobile hot‑update topics were discussed. Bugly introduced several internal hot‑update frameworks, and the author saw the practices of Meituan, Qunar, and Weibo regarding application hot‑updates.
Last week the author shared the article "Meituan Dianping Hybrid Construction"; this week the focus is on the "Cross‑Platform ListView Performance Optimization" presented by Qunar's iOS Technical Director.
Main Content
Speaker: Jiang Zhuo, who joined Qunar in 2011 and has been leading the iOS hotel team since 2013, focusing on cross‑platform, iOS architecture, and client infrastructure.
Qunar's RN Journey
Initially Qunar used Hybrid solutions, but the experience was not comparable to native performance. After RN 0.22 was released in March 2016, Qunar launched its first business‑critical RN page on the hotel client home screen, using the migration as an opportunity for hot‑fixes and code refactoring.
To date, about 18 hotel‑related pages in Qunar's travel app have been built with RN.
How RN Implements ListView
RN's ListView is built on RCTScrollView, mirroring UIKit's DataSource pattern. A key feature inherited from RCTView is the removeClippedSubviews flag. When set to true, RN removes views that scroll off‑screen by calling updateClippedSubviews.
RN manages UI elements through RCTUIManager to reduce view creation and destruction during DOM diffing.
What ListView Adds
ListView introduces two properties: initialListSize (items loaded on the first screen) and pageSize (items loaded on each subsequent request), aiming to minimize initial load time. It also wraps Section Header, Header, Footer, and onScroll handling, dynamically adding row views as the list scrolls.
What Is Missing Compared to UITableView
Unlike UITableView, RN's ListView does not expose a native reuse mechanism. The example on the ListView website uses RecyclerViewBackedScrollView, which on Android relies on RecyclerView for view reuse, but RN's implementation merely caches views without true recycling.
Solutions
Qunar explored two directions to address RN's reuse limitation.
1. JavaScript‑Based Cell Reuse
The front‑end team attempted to implement reuse logic in JS by listening to onScroll and moving off‑screen cells back into the view hierarchy. This approach works only when cells have uniform height and simple content; otherwise the frequent bridge calls between JS and native exceed the 16 ms frame budget, leading to poor performance.
Drawbacks
High frequency of onScroll updates causes excessive JS‑to‑native communication, and asynchronous handling in JS prevents achieving the desired smoothness.
2. Bridge a Native UITableView
The native team created a bridge that implements the RCTComponent protocol, focusing on the two essential methods:
- (void)insertReactSubview:(id<RCTComponent>)subview atIndex:(NSInteger)atIndex;
- (void)removeReactSubview:(id<RCTComponent>)subview;These methods drive RN's DOM diff. By creating virtual views that conform to RCTComponent, the bridge translates them into real native views and inserts them into a UITableView. Tags are added to subviews so that data updates can be applied by locating the view via its tag.
All TableViewDataSource methods were replicated in RN, allowing developers to configure callbacks similarly to native code.
Drawbacks
The solution only maps a limited set of native components (Label and Image). Complex native controls cannot be used directly, and the approach is iOS‑specific, making it unsuitable for Android where ListView works differently.
Weex ListView
Weex implements ListView natively on both iOS and Android. On iOS it uses a single reuseIdentifier but essentially caches cells without true reuse. On Android it leverages RecyclerView, generating view types via generateViewType, creating ViewHolder s, and updating data in onBindViewHolder through bindData and updateProperties. This achieves proper reuse on Android.
Conclusion
Among the explored methods, the native UITableView bridge offers the best memory efficiency, while the JavaScript solution is limited by bridge overhead. Weex provides the most performant reuse on Android, and RN’s pure‑JS implementation is the most portable across platforms.
Overall, RN’s JS‑based approach is easier to share across iOS and Android, but performance and reuse are superior when native components are bridged or when using frameworks like Weex.
Tencent TDS Service
TDS Service offers client and web front‑end developers and operators an intelligent low‑code platform, cross‑platform development framework, universal release platform, runtime container engine, monitoring and analysis platform, and a security‑privacy compliance suite.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
