React Native 0.76 version comes with a comprehensive architectural overhaul to enhance performance, security, and provide a smoother user experience. With this update, the Fabric Renderer and TurboModules make in-app transitions faster and smoother, while memory usage is optimized through auto-loaded modules. The integration of the new JSI and Hermes engine, in particular, ensures secure, high-performance communication with native modules. A closer look at the details and practical applications reveals a versatile toolkit for React Native developers.
Key Features:
- TurboModules: Loads only the required modules.
- Fabric Renderer: Full support for React 18’s features with concurrent rendering.
- Hermes Integration: Low memory usage and fast processing.
1. Fabric Renderer
Fabric is a redesigned UI rendering system in React Native. It removes the traditional asynchronous bridge, which often caused bottlenecks, enabling JavaScript and native components to work more directly together.
- Synchronous Communication: Fabric establishes a direct connection with native components, enabling instantaneous, real-time updates.
- Partial Tree Updates: Unlike the previous architecture, Fabric renders only the components that have changed, reducing the need for re-rendering and conserving resources.
- Goal: To enable React 18's automatic batching and concurrent rendering with a new-generation rendering engine.
Example:
import React, { useState, useEffect } from 'react';
import { Text, View, Platform } from 'react-native';
const useSynchronizedFabricComponent = (text) => {
// Custom hook simulating Fabric's synchronous behavior
return text.toUpperCase(); // Fabric instantly reflects this update
};
const FabricEnhancedComponent = () => {
const [message, setMessage] = useState("Hello Fabric!");
const displayMessage = useSynchronizedFabricComponent(message);
useEffect(() => {
if (Platform.OS === 'android') {
setMessage("Fabric Running on Android!");
}
}, []);
return (
<View style={{ padding: 20 }}>
<Text>{displayMessage}</Text>
</View>
);
};
export default FabricEnhancedComponent;
In this example, Fabric instantly renders displayMessage
, making the UI smoother with synchronous processing.
2. Turbo Modules and JSI
Turbo Modules replace the traditional bridge with a more efficient system that accelerates interaction between JavaScript and native code. This system ensures that only the required modules are loaded, thus reducing memory usage.
- On-Demand Loading: Modules are loaded only when needed, which reduces memory usage.
- Performance Boost: JavaScript calls to native functions are faster, minimizing interaction delays.
- Goal: To eliminate synchronization issues caused by the bridge architecture and provide fast, type-safe access to modules.
- Details: The JavaScript Interface (JSI) enables direct, bridge-free communication between JavaScript and native modules. TurboModules allow for performance gains by loading modules only as needed.
Example:
// Directly calling the native module from JavaScript
import { NativeModules } from 'react-native';
const { ExampleModule } = NativeModules;
ExampleModule.showToast("Hello!");
Example:
import { NativeModules } from 'react-native';
const { BatteryStatusModule } = NativeModules;
const fetchBatteryLevel = async () => {
try {
const level = await BatteryStatusModule.getBatteryLevel();
console.log("Current Battery Level:", level);
} catch (error) {
console.error("Failed to retrieve battery level", error);
}
};
fetchBatteryLevel();
In this example, Turbo Modules make more efficient use of memory by loading BatteryStatusModule
only when called, thus speeding up native calls.
3. New Configuration: Codegen and Native Components
Codegen is an automatic code generation tool that ensures type safety between JavaScript and native code. Working alongside TypeScript or Flow, it guarantees code accuracy and reduces the risk of errors.
- Type Safety: Ensures strong type safety between JavaScript and native code.
- Automatic Code Generation: Automates binding code, enhancing consistency and reducing repetitive tasks.
Setup and Configuration:
Run yarn react-native codegen
in your project folder to create the necessary files and bindings.
You typically configure codegen in the react-native.config.js
file.
yarn react-native codegen
// react-native.config.js
module.exports = {
dependencies: {
// Native modules that require code generation
'react-native-sample-module': {
platforms: {
ios: {
// Path to the codegen file for iOS
codegenConfig: {
name: 'SampleModule',
jsSrcsDir: './src',
},
},
android: {
// Path to the codegen file for Android
codegenConfig: {
name: 'SampleModule',
jsSrcsDir: './src',
},
},
},
},
},
};
In this example, SampleModule is a native component that Codegen has automatically generated the necessary bridge connections and types for. This allows you to directly call SampleModule in JavaScript with full type safety and without needing to manually write bridge code.
// Importing the native module generated by Codegen
import { SampleModule } from 'react-native-sample-module';
// Directly calling a method on SampleModule, with type safety ensured by Codegen
const getSampleData = async () => {
try {
const data = await SampleModule.fetchData();
console.log("Fetched Data:", data);
} catch (error) {
console.error("Failed to fetch data from SampleModule", error);
}
};
// Invoking the function to get sample data
getSampleData();
SampleModule.fetchData() is called directly, with Codegen ensuring type-safe access to the native module.
Any issues with fetching data from the native module are caught in the catch block for error handling.
import { TurboModule, TurboModuleRegistry } from 'react-native';
// Defining type safety for a native module using Codegen
interface BatteryModule extends TurboModule {
getBatteryStatus(): Promise<string>;
}
export default TurboModuleRegistry.get<BatteryModule>('BatteryModule');
Here, Codegen makes the getBatteryStatus
function available with full type checking, reducing the risk of errors between JavaScript and native layers.
4. Enhanced Bridge
The new Bridge architecture optimizes interactions between native and JavaScript. By reducing dependency on the bridge, it enables critical tasks to be handled directly within native components.
- Reduced Dependency: Some data processing occurs directly on the native side.
- Native-Oriented Processing: Prioritizes handling of UI and data processing in native code, enhancing speed.
Advantages of the New Bridge Structure
With this innovation, applications can now communicate much faster with frequently used native functions.
Example:
import { DeviceEventEmitter } from 'react-native';
// Listening to a native event without needing the bridge
DeviceEventEmitter.addListener('BatteryLowWarning', (data) => {
console.log("Battery at critical level:", data.level);
});
In this example, a direct connection is established with the native side, and it responds immediately to the event.
Example:
import { NativeModules, DeviceEventEmitter } from 'react-native';
import React, { useEffect } from 'react';
import { Text } from 'react-native';
const { DeviceLocationModule } = NativeModules;
const LocationComponent = () => {
// Accessing location information synchronously
const initialLocation = DeviceLocationModule.getCurrentLocation();
useEffect(() => {
// Listening for location changes
const locationSubscription = DeviceEventEmitter.addListener(
'LocationChange',
(newLocation) => {
console.log(`Updated location: ${newLocation.latitude}, ${newLocation.longitude}`);
}
);
return () => {
locationSubscription.remove();
};
}, []);
return <Text>Initial Location: {initialLocation.latitude}, {initialLocation.longitude}</Text>;
};
export default LocationComponent;
Synchronous Access: The getCurrentLocation()
call provides an instant response at app startup, speeding up the launch time.
Event Handling: The LocationChange
event establishes a direct connection with the native side, reflecting location updates instantly without bridge delays.
This model enhances performance, especially for applications requiring real-time data streaming.
5. Enhanced Hermes Engine
Hermes is a JavaScript engine tailored for React Native and optimized for mobile. With the 0.76 updates, memory management and startup performance have improved, app size has been reduced, and startup time has been accelerated.
- Smaller App Size: Hermes runtime becomes more compact.
- Faster Startup: Starts faster with memory optimizations.
-
Advanced Setup: To enable Hermes, update your
android/app/build.gradle
file:
// Enabling Hermes
// android/app/build.gradle
project.ext.react = [
enableHermes: true // Enable Hermes for performance
]
6. Concurrent Rendering and Suspense Support
Concurrent Rendering brings React’s priority-based rendering capabilities to React Native. This feature ensures that user interactions happen smoothly without interruptions.
- Render on Demand: Heavy UI components are rendered in parts.
- User Priority: Prioritizes user actions to provide a better experience.
Example:
import React, { useTransition } from 'react';
const Component = () => {
const [isPending, startTransition] = useTransition();
const handleLongProcess = () => {
startTransition(() => {
// Executing a heavy process concurrently
performHeavyCalculations();
});
};
return (
<div>
<button onClick={handleLongProcess}>Start Heavy Process</button>
{isPending && <p>Processing...</p>}
</div>
);
};
export default Component;
In this example, useTransition
allows the heavy performHeavyCalculations
function to run concurrently without blocking user interactions. The isPending
flag helps indicate if the process is ongoing, providing feedback to the user, which improves the overall experience.
a. Suspense Support
import React, { Suspense } from 'react';
const DataComponent = React.lazy(() => import('./DataComponent'));
const App = () => {
return (
<div>
<Suspense fallback={<p>Loading data...</p>}>
<DataComponent />
</Suspense>
</div>
);
};
export default App;
In this example, Suspense
is used to handle asynchronous data loading. The DataComponent
is loaded lazily, and while it’s being fetched, a fallback message ("Loading data...") is displayed. This keeps the UI responsive and provides a smoother experience during data fetching.
7. New Event Loop and Performance Improvements
Goal: Synchronizing UI updates, making animations smoother, and providing faster responses to user inputs.
Details: The Event Loop is optimized to reduce the load on the main processor, allowing for quick responses to user actions. This provides a major advantage in animations and interactions that require high performance.
Example:
// Automatic batching with React 18
setCounter((c) => c + 1);
setCounter((c) => c + 1); // Increases with a single rendering
a. Automatic Batching:
Normally, multiple state updates can trigger separate renders. However, with automatic batching, these updates are grouped together, avoiding unnecessary renders.
import React, { useState } from 'react';
import { Button, Text, View } from 'react-native';
function Counter() {
const [count, setCount] = useState(0);
const [anotherState, setAnotherState] = useState(false);
const handleClick = () => {
setCount(c => c + 1);
setAnotherState(s => !s);
// With automatic batching, both state updates happen within a single render.
};
return (
<View>
<Text>Count: {count}</Text>
<Button title="Update" onPress={handleClick} />
</View>
);
}
export default Counter;
In this example, the handleClick
function includes two different state updates. With React Native 0.76’s automatic batching, these two updates are performed in a single render.
8. useLayoutEffect Support
useLayoutEffect
is a React hook fully supported in React Native 0.76's New Architecture update. It provides synchronous access to layout information for DOM or native components. Unlike useEffect
, which runs after rendering, useLayoutEffect
executes during the commit phase. This allows you to adjust UI component positions immediately, as it works during rendering rather than post-render like useEffect
.
Example:
If you want to place a tooltip in the correct position on the screen, you can use useLayoutEffect
to position the element before rendering completes. This provides more precise placement, especially for animations or tooltips where positioning is crucial.
import React, { useLayoutEffect, useRef, useState } from 'react';
import { View, Text, StyleSheet } from 'react-native';
function TooltipExample() {
const tooltipRef = useRef(null);
const [tooltipPosition, setTooltipPosition] = useState({ top: 0, left: 0 });
useLayoutEffect(() => {
tooltipRef.current.measure((x, y, width, height, pageX, pageY) => {
setTooltipPosition({ top: pageY + height, left: pageX });
});
}, []);
return (
<View ref={tooltipRef} style={styles.button}>
<Text>Button</Text>
<View style={[styles.tooltip, tooltipPosition]}>
<Text>Tooltip content</Text>
</View>
</View>
);
}
const styles = StyleSheet.create({
button: { padding: 10, backgroundColor: 'lightblue' },
tooltip: { position: 'absolute', backgroundColor: 'black', color: 'white', padding: 5 },
});
export default TooltipExample;
In this example, useLayoutEffect
ensures that the tooltip is positioned correctly right before rendering. By using measure()
to get the element’s position, it is accurately placed on the screen.
9. Enhanced iOS and Android Support
- iOS: The new architecture improves memory management and resource prioritization, making animations and UI interactions smoother on iOS.
- Android: Integration of Turbo Modules and Fabric has been simplified on Android.
Conclusion
React Native 0.76 marks a significant step forward in the development process with performance improvements and new features. The New Architecture supports modern React 18 features like Suspense, Transitions, and Automatic Batching, enabling faster and smoother UIs. With Concurrent Rendering and Suspense Support, UI updates are quicker and more fluid, while Automatic Batching reduces unnecessary renders. useLayoutEffect
simplifies synchronous component positioning. The removal of the old bridge and direct access to native modules via JavaScript Interface (JSI) and the new C++-based Native Module System enables type-safe access. Additionally, automatic type definitions via Codegen and an enhanced event loop make React Native more aligned with modern standards. Finally, temporary bridge compatibility allows for a gradual transition, providing flexibility during adaptation to the new architecture. These updates enhance developer productivity and improve user experience by making it faster and more seamless.
Reference: React Native 0.76 — New Architecture
Happy coding! 🚀
Top comments (0)