Razorpay Sourav Yadav Feb 7, 2022

Achieving 60 FPS in React Native with Imperative Manipulation

Article Summary

Sourav Yadav from Razorpay discovered that React Native's declarative approach was causing frame rates to plummet to 0 FPS. His solution? Break the rules and go imperative.

React Native's declarative API is convenient, but it comes with a hidden cost: the reconciliation algorithm can become a performance bottleneck when dealing with thousands of nodes. Razorpay's engineering team tested both declarative and imperative approaches to find where the breaking point occurs and how to fix it.

Key Takeaways

Critical Insight

When React Native apps hit performance walls with complex UIs, setNativeProps can maintain 60 FPS by skipping reconciliation, but only use it as a last resort after exhausting declarative optimizations.

The article includes side-by-side performance comparisons showing exactly when React's diffing algorithm becomes your enemy, plus the specific scenarios where imperative manipulation backfires.

About This Article

Problem

React's diffing algorithm runs in O(n) complexity, which caused performance issues when rendering large component trees. Adding a TextInput to 3000 animated nodes dropped the JS thread to 7 FPS and the UI thread to 38 FPS during state updates.

Solution

Sourav Yadav's team used setNativeProps to directly manipulate native view properties. This bypassed React's reconciliation process and gave them imperative control over component visibility and attributes.

Impact

Imperative manipulation kept both the JS and UI threads at a consistent 60 FPS with 3000 nodes. The frame drops that happened with the declarative approach disappeared, and TextInput operations ran without lag.