Virtual DOM and Diff Algorithm Optimization
Virtual DOM Principles and Optimization Strategies
// Optimization strategy example: Reducing unnecessary re-renders
import React, { memo, useCallback } from ' react ' ;
// Use memo to prevent unnecessary re-renders
const ExpensiveComponent = memo (({ data , onClick }) => {
// Component implementation
});
// Use useCallback to cache function references
const ParentComponent = () => {
const handleClick = useCallback (() => {
// Handle click event
}, []); // Empty dependency array ensures function reference stability
return < ExpensiveComponent data ={ data } onClick ={ handleClick } />;
};
Manual Control of Component Updates
// Use shouldComponentUpdate (class components) or React.memo (function components) for fine-grained control
class MyComponent extends React . Component {
shouldComponentUpdate ( nextProps , nextState ) {
// Update only when specific props or state change
return nextProps.id !== this .props.id || nextState.value !== this .state.value;
}
render () {
// Component implementation
}
}
Basic Optimization Configuration
import React from ' react ' ;
import { FlatList, View, Text } from ' react-native ' ;
const OptimizedFlatList = () => {
// Use keyExtractor to provide stable keys
// Use getItemLayout to optimize fixed-height lists
// Use initialNumToRender to control initial render count
// Use windowSize to control render window size
// Use maxToRenderPerBatch to control batch rendering count
// Use updateCellsBatchingPeriod to control batch update interval
return (
< FlatList
data ={ data }
keyExtractor ={ ( item ) => item.id. toString () } // Stable key
getItemLayout ={ ( data , index ) => (
{ length: ITEM_HEIGHT , offset: ITEM_HEIGHT * index , index }
) } // Fixed-height list optimization
initialNumToRender ={ 10 } // Initial render count
windowSize ={ 5 } // Render window size (items rendered outside the screen)
maxToRenderPerBatch ={ 5 } // Maximum items rendered per batch
updateCellsBatchingPeriod ={ 50 } // Batch update interval (ms)
renderItem ={ ({ item }) => < ListItem item ={ item } /> }
/>
);
};
Advanced Optimization Techniques
// Use React.memo to optimize list items
const ListItem = memo (({ item }) => {
// List item implementation
});
// Use separate data sources (e.g., FlashList) instead of FlatList
// Use virtualization techniques for complex list items
// Avoid inline functions and objects in list items
// Use shouldComponentUpdate or React.memo to optimize list item rendering
Memory Leak Detection and Analysis
Common Causes of Memory Leaks
// Common memory leak scenarios
class LeakExample extends React . Component {
intervalId = null ;
componentDidMount () {
// Forgetting to clear the timer
this .intervalId = setInterval (() => {
// Timer task
}, 1000 );
}
// Missing componentWillUnmount cleanup
}
// Closure reference causing a leak
function createLeak () {
const bigData = new Array ( 1000000 ). fill ( ' data ' );
return () => {
// Closure retains reference to bigData
console. log ( ' Closure with memory leak ' );
};
}
// Using React Native Debugger to detect memory leaks
// 1. Install React Native Debugger
// 2. Connect the app
// 3. Use the Memory tab for heap snapshot comparison
// Using Chrome DevTools Memory panel
// 1. Enable remote debugging when running the app
// 2. Use the Heap Snapshot feature
// 3. Compare heap states at different points in time
// Using performance monitoring tools
// 1. Reactotron
// 2. Flipper
// 3. Firebase Performance Monitoring
Image Loading and Caching Optimization (react-native-fast-image)
Basic Optimization Configuration
import FastImage from ' react-native-fast-image ' ;
const OptimizedImage = () => (
< FastImage
style ={ { width : 200 , height : 200 } }
source ={ {
uri : ' https://example.com/image.jpg ' ,
priority : FastImage.priority.high, // Loading priority
cache : FastImage.cacheControl.immutable, // Cache strategy
} }
resizeMode ={ FastImage.resizeMode.contain }
/>
);
Advanced Cache Strategies
// Custom cache control
const CustomCacheImage = () => (
< FastImage
source ={ {
uri : ' https://example.com/image.jpg ' ,
priority : FastImage.priority.normal,
cache : FastImage.cacheControl.web, // Use web cache strategy
} }
/>
);
// Preload images
import { useEffect } from ' react ' ;
const PreloadImages = () => {
useEffect (() => {
const preload = async () => {
await FastImage. preload ([
{ uri : ' https://example.com/image1.jpg ' },
{ uri : ' https://example.com/image2.jpg ' },
]);
};
preload ();
}, []);
return null ;
};
Basic Animation Optimization
import React, { useRef } from ' react ' ;
import { Animated, View, Easing } from ' react-native ' ;
const OptimizedAnimation = () => {
const fadeAnim = useRef ( new Animated. Value ( 0 )).current;
const fadeIn = () => {
// Use useNativeDriver: true to move animation to native thread
Animated. timing (fadeAnim, {
toValue : 1 ,
duration : 1000 ,
easing : Easing.ease,
useNativeDriver : true , // Key optimization point
}). start ();
};
return (
< View >
< Animated.View
style ={ {
opacity : fadeAnim,
transform : [{ scale : fadeAnim }], // Note: Not all properties support native driver
} }
/>
< Button title = " Fade In " onPress ={ fadeIn } />
</ View >
);
};
Properties Supporting Native Driver
// Style properties supporting useNativeDriver
const supportedStyles = [
' opacity ' , // Opacity
' transform ' , // Transform (translate, rotate, scale)
' backgroundColor ' , // Background color (Android)
' width ' , // Width (iOS)
' height ' , // Height (iOS)
];
// Properties not supporting native driver
const unsupportedStyles = [
' color ' , // Text color
' borderRadius ' , // Border radius
' shadowOpacity ' , // Shadow opacity
' textShadowOffset ' , // Text shadow offset
];
Native Module Development
Creating Native Modules (Android, iOS)
Android Native Module
// ToastModule.java
package com.example.app ;
import android.widget.Toast;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
public class ToastModule extends ReactContextBaseJavaModule {
private final ReactApplicationContext reactContext;
public ToastModule ( ReactApplicationContext reactContext ) {
super (reactContext);
this .reactContext = reactContext;
}
@ Override
public String getName () {
return " ToastModule " ;
}
@ ReactMethod
public void show ( String message , int duration ) {
Toast. makeText (reactContext, message, duration). show ();
}
}
iOS Native Module
// ToastModule.m
#import < React/RCTBridgeModule.h >
@interface RCT_EXTERN_MODULE (ToastModule, NSObject)
RCT_EXTERN_METHOD (show:(NSString * )message duration:(NSInteger) duration )
@end
Calling Native APIs (Camera, Sensor)
Android Camera API Call
// CameraModule.java
@ ReactMethod
public void takePicture ( Promise promise) {
try {
// Get camera instance
Camera camera = Camera. open ();
// Set camera parameters
Camera . Parameters parameters = camera. getParameters ();
parameters. setPictureFormat (ImageFormat.JPEG);
camera. setParameters (parameters);
// Take picture
camera. takePicture ( null , null , (data, camera) -> {
// Convert image data to Base64
String base64Image = Base64. encodeToString (data, Base64.DEFAULT);
promise. resolve (base64Image);
// Release camera resources
camera. release ();
});
} catch ( Exception e ) {
promise. reject ( " CAMERA_ERROR " , e);
}
}
iOS Sensor API Call
// SensorModule.m
#import < CoreMotion/CoreMotion.h >
@implementation SensorModule {
CMMotionManager * _motionManager;
}
RCT_EXPORT_MODULE ();
- (instancetype)init {
self = [super init];
if (self) {
_motionManager = [[CMMotionManager alloc] init];
}
return self;
}
RCT_EXPORT_METHOD (startAccelerometerUpdates:(RCTResponseSenderBlock) callback ) {
if ( ! _motionManager.accelerometerAvailable) {
callback (@[@ " Accelerometer not available " ]);
return ;
}
[_motionManager startAccelerometerUpdatesToQueue:[NSOperationQueue mainQueue]
withHandler: ^ (CMAccelerometerData * accelerometerData, NSError * error) {
if (error) {
callback (@[error.localizedDescription]);
return ;
}
CMAcceleration acceleration = accelerometerData.acceleration;
NSDictionary * result = @{
@ " x " : @(acceleration.x),
@ " y " : @(acceleration.y),
@ " z " : @(acceleration.z)
};
callback (@[result]);
}];
}
@end
Native Component Encapsulation (Custom Native UI Components)
Android Custom View
// CustomViewManager.java
package com.example.app ;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.View;
import com.facebook.react.uimanager.SimpleViewManager;
import com.facebook.react.uimanager.ThemedReactContext;
public class CustomViewManager extends SimpleViewManager <View> {
@ Override
public String getName () {
return " CustomView " ;
}
@ Override
protected View createViewInstance ( ThemedReactContext reactContext ) {
return new CustomDrawableView (reactContext);
}
public static class CustomDrawableView extends View {
private final Paint paint = new Paint ();
public CustomDrawableView ( Context context ) {
super (context);
paint. setColor (Color.RED);
paint. setStyle (Paint.Style.FILL);
}
@ Override
protected void onDraw ( Canvas canvas ) {
super . onDraw (canvas);
canvas. drawCircle ( 100 , 100 , 50 , paint);
}
}
}
iOS Custom UIView
// CustomViewManager.m
#import < React/RCTViewManager.h >
@interface CustomViewManager : RCTViewManager
@end
@implementation CustomViewManager
RCT_EXPORT_MODULE ()
- (UIView * )view {
return [[CustomDrawableView alloc] init];
}
@end
// CustomDrawableView.m
#import " CustomDrawableView.h "
@implementation CustomDrawableView {
CAShapeLayer * _circleLayer;
}
- (instancetype)init {
self = [super init];
if (self) {
_circleLayer = [CAShapeLayer layer];
_circleLayer.fillColor = [UIColor redColor].CGColor;
UIBezierPath * path = [UIBezierPath bezierPathWithOvalInRect: CGRectMake ( 0 , 0 , 100 , 100 )];
_circleLayer.path = path.CGPath;
[self.layer addSublayer:_circleLayer];
}
return self;
}
- ( void )layoutSubviews {
[super layoutSubviews];
_circleLayer.frame = CGRectMake ( 50 , 50 , 100 , 100 );
}
@end
Using TurboModules
Android TurboModules
// ToastModuleSpec.java
package com.example.app ;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.turbomodule.core.interfaces.TurboModule;
public interface ToastModuleSpec extends TurboModule {
void show ( String message , int duration , Promise promise );
}
// ToastModule.java
package com.example.app ;
import android.widget.Toast;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.module.annotations.ReactModule;
@ ReactModule ( name = ToastModuleSpec.NAME)
public class ToastModule extends NativeToastModuleSpec implements ToastModuleSpec {
public ToastModule ( ReactApplicationContext reactContext ) {
super (reactContext);
}
@ Override
public void show ( String message , int duration , Promise promise ) {
try {
Toast. makeText ( getReactApplicationContext (), message, duration). show ();
promise. resolve ( null );
} catch ( Exception e ) {
promise. reject ( " TOAST_ERROR " , e);
}
}
}
iOS TurboModules
// ToastModule.h
#import < React/RCTTurboModule.h >
@interface ToastModule : NSObject < RCTTurboModule >
- ( void )show:(NSString * )message duration:(NSInteger)duration resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject;
@end
// ToastModule.mm
#import " ToastModule.h "
#import < React/RCTConvert.h >
@implementation ToastModule
RCT_EXPORT_MODULE ()
- ( void )show:(NSString * )message duration:(NSInteger)duration resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
dispatch_async ( dispatch_get_main_queue (), ^ {
UIAlertController * alert = [UIAlertController alertControllerWithTitle:@ " Toast "
message:message
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction * ok = [UIAlertAction actionWithTitle:@ " OK "
style:UIAlertActionStyleDefault
handler:nil];
[alert addAction:ok];
[[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:alert
animated:YES
completion:nil];
resolve (nil);
});
}
@end
Reducing Bridge Calls
// Bad practice: Frequent bridge calls
const badExample = () => {
dataArray. forEach ( item => {
NativeModules.ToastModule. show (item.message); // Crosses bridge each loop
});
};
// Good practice: Batch processing
const goodExample = () => {
const messages = dataArray. map ( item => item.message);
NativeModules.ToastModule. showBatch (messages); // Single bridge call
};
Using Batch Processing and Caching
// Android batch processing example
@ ReactMethod
public void showBatch (ReadableArray messages) {
for (int i = 0 ; i < messages. size (); i ++ ) {
String message = messages. getString (i);
Toast. makeText (reactContext, message, Toast. LENGTH_SHORT ). show ();
}
}
// iOS batch processing example
RCT_EXPORT_METHOD (showBatch:(NSArray * )messages) {
for (NSString * message in messages) {
UIAlertController * alert = [UIAlertController
alertControllerWithTitle:@ " Batch Toast "
message:message
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction * ok = [UIAlertAction
actionWithTitle:@ " OK "
style:UIAlertActionStyleDefault
handler:nil];
[alert addAction:ok];
[[UIApplication sharedApplication].keyWindow.rootViewController
presentViewController:alert
animated:YES
completion:nil];
}
}
Animations and Gestures
Advanced Usage of Animated API (Interpolation, Combined Animations)
Interpolation Animation
import React, { useRef } from ' react ' ;
import { Animated, View, Easing } from ' react-native ' ;
const InterpolationExample = () => {
const animValue = useRef ( new Animated. Value ( 0 )).current;
const startAnimation = () => {
Animated. timing (animValue, {
toValue : 1 ,
duration : 1000 ,
easing : Easing. bezier ( 0.25 , 0.1 , 0.25 , 1 ),
useNativeDriver : true ,
}). start ();
};
// Use interpolation to map 0-1 values to different ranges
const rotateInterpolate = animValue. interpolate ({
inputRange : [ 0 , 1 ],
outputRange : [ ' 0deg ' , ' 360deg ' ],
});
const scaleInterpolate = animValue. interpolate ({
inputRange : [ 0 , 0.5 , 1 ],
outputRange : [ 1 , 1.5 , 1 ],
});
return (
< View >
< Animated.View
style ={ {
transform : [
{ rotate : rotateInterpolate },
{ scale : scaleInterpolate },
],
} }
/>
< Button title = " Start Animation " onPress ={ startAnimation } />
</ View >
);
};
Combined Animation
import React, { useRef } from ' react ' ;
import { Animated, View, Easing } from ' react-native ' ;
const CombinedAnimation = () => {
const fadeAnim = useRef ( new Animated. Value ( 0 )).current;
const translateXAnim = useRef ( new Animated. Value ( 0 )).current;
const startCombinedAnimation = () => {
Animated. parallel ([
// Fade-in animation
Animated. timing (fadeAnim, {
toValue : 1 ,
duration : 1000 ,
useNativeDriver : true ,
}),
// Translate animation
Animated. timing (translateXAnim, {
toValue : 100 ,
duration : 1500 ,
easing : Easing. elastic ( 1 ),
useNativeDriver : true ,
}),
]). start ();
};
return (
< View >
< Animated.View
style ={ {
opacity : fadeAnim,
transform : [{ translateX : translateXAnim }],
} }
/>
< Button title = " Start Combined Animation " onPress ={ startCombinedAnimation } />
</ View >
);
};
Gesture Handling (PanResponder, react-native-gesture-handler)
PanResponder Basic Implementation
import React, { useRef } from ' react ' ;
import { View, PanResponder, Text } from ' react-native ' ;
const PanResponderExample = () => {
const position = useRef ({ x : 0 , y : 0 }).current;
const panResponder = useRef (
PanResponder. create ({
onStartShouldSetPanResponder : () => true ,
onMoveShouldSetPanResponder : () => true ,
onPanResponderGrant : () => {
// Gesture start
},
onPanResponderMove : ( evt , gestureState ) => {
position.x += gestureState.dx;
position.y += gestureState.dy;
},
onPanResponderRelease : () => {
// Gesture end
},
})
).current;
return (
< View
{... panResponder.panHandlers }
style ={ {
width : 100 ,
height : 100 ,
backgroundColor : ' red ' ,
position : ' absolute ' ,
left : position.x,
top : position.y,
} }
/>
);
};
Advanced Usage of react-native-gesture-handler
import React from ' react ' ;
import { View, Text } from ' react-native ' ;
import { Gesture, GestureDetector } from ' react-native-gesture-handler ' ;
import Animated, { useSharedValue, withSpring } from ' react-native-reanimated ' ;
const GestureHandlerExample = () => {
const translateX = useSharedValue ( 0 );
const translateY = useSharedValue ( 0 );
const panGesture = Gesture. Pan ()
. onUpdate (( event ) => {
translateX.value = event.translationX;
translateY.value = event.translationY;
})
. onEnd (() => {
// Add spring animation
translateX.value = withSpring ( 0 );
translateY.value = withSpring ( 0 );
});
return (
< View style ={ { flex : 1 , justifyContent : ' center ' , alignItems : ' center ' } } >
< GestureDetector gesture ={ panGesture } >
< Animated.View
style ={ {
width : 100 ,
height : 100 ,
backgroundColor : ' blue ' ,
transform : [
{ translateX : translateX.value },
{ translateY : translateY.value },
],
} }
/>
</ GestureDetector >
</ View >
);
};
Physics-Based Animations and Spring Animations
Spring Animation Implementation
import React, { useRef } from ' react ' ;
import { Animated, View, Button, Easing } from ' react-native ' ;
const SpringAnimation = () => {
const animValue = useRef ( new Animated. Value ( 0 )).current;
const startSpringAnimation = () => {
animValue. setValue ( 0 ); // Reset value
Animated. spring (animValue, {
toValue : 1 ,
friction : 4 , // Control spring oscillation
tension : 30 , // Control spring speed
useNativeDriver : true ,
}). start ();
};
return (
< View >
< Animated.View
style ={ {
transform : [{ scale : animValue }],
} }
/>
< Button title = " Start Spring Animation " onPress ={ startSpringAnimation } />
</ View >
);
};
Physics-Based Animation Effects
import React, { useRef } from ' react ' ;
import { Animated, View, Button } from ' react-native ' ;
const PhysicsAnimation = () => {
const animValue = useRef ( new Animated. Value ( 0 )).current;
const startPhysicsAnimation = () => {
animValue. setValue ( 0 );
Animated. timing (animValue, {
toValue : 1 ,
duration : 1000 ,
easing : ( t ) => t * t * ( 3 - 2 * t), // Custom easing function to simulate physics effect
useNativeDriver : true ,
}). start ();
};
return (
< View >
< Animated.View
style ={ {
opacity : animValue,
transform : [
{
translateY : animValue. interpolate ({
inputRange : [ 0 , 1 ],
outputRange : [ 100 , 0 ],
}),
},
],
} }
/>
< Button title = " Start Physics Animation " onPress ={ startPhysicsAnimation } />
</ View >
);
};
Interactive Animations (Drag, Scale)
Drag and Scale Combination
import React, { useRef } from ' react ' ;
import { View, PanResponder, Animated } from ' react-native ' ;
const DragAndScaleExample = () => {
const pan = useRef ( new Animated. ValueXY ()).current;
const scale = useRef ( new Animated. Value ( 1 )).current;
const panResponder = useRef (
PanResponder. create ({
onStartShouldSetPanResponder : () => true ,
onMoveShouldSetPanResponder : () => true ,
onPanResponderGrant : () => {
// Fix current scale when starting drag
pan. setOffset ({ x : pan.x._value, y : pan.y._value });
pan. setValue ({ x : 0 , y : 0 });
},
onPanResponderMove : ( evt , gestureState ) => {
pan.x. setValue (gestureState.dx);
pan.y. setValue (gestureState.dy);
},
onPanResponderRelease : () => {
// Clear offset when drag ends
pan. flattenOffset ();
},
})
).current;
const handleScale = ( scaleValue ) => {
scale. setValue (scaleValue);
};
return (
< View >
< Animated.View
{... panResponder.panHandlers }
style ={ {
transform : [
{ translateX : pan.x },
{ translateY : pan.y },
{ scale : scale },
],
} }
/>
{ /* Scale control buttons */ }
< Button title = " Scale + " onPress ={ () => handleScale (scale._value + 0.1 ) } />
< Button title = " Scale - " onPress ={ () => handleScale (Math. max ( 0.5 , scale._value - 0.1 )) } />
</ View >
);
};
Using Native Driver
import React, { useRef } from ' react ' ;
import { Animated, View, Easing } from ' react-native ' ;
const NativeDriverExample = () => {
const animValue = useRef ( new Animated. Value ( 0 )).current;
const startAnimation = () => {
// Key optimization: Use useNativeDriver
Animated. timing (animValue, {
toValue : 1 ,
duration : 1000 ,
easing : Easing. bezier ( 0.25 , 0.1 , 0.25 , 1 ),
useNativeDriver : true , // Move animation to native thread
}). start ();
};
return (
< View >
< Animated.View
style ={ {
opacity : animValue,
transform : [{ rotate : animValue. interpolate ({
inputRange : [ 0 , 1 ],
outputRange : [ ' 0deg ' , ' 360deg ' ],
}) }],
} }
/>
< Button title = " Start Optimized Animation " onPress ={ startAnimation } />
</ View >
);
};
Avoiding Layout Jank
import React, { useRef, useState } from ' react ' ;
import { Animated, View, Text, Button } from ' react-native ' ;
const AvoidLayoutJank = () => {
const animValue = useRef ( new Animated. Value ( 0 )).current;
const [text, setText] = useState ( ' Initial Text ' );
// Bad practice: Changing layout properties during animation
const badAnimation = () => {
Animated. timing (animValue, {
toValue : 1 ,
duration : 1000 ,
useNativeDriver : false , // Note: Intentionally using false to demonstrate issue
}). start (() => {
// Changing layout-related properties after animation (causes layout jank)
setText ( ' Updated Text ' );
});
};
// Good practice: Separate animation and layout changes
const goodAnimation = () => {
Animated. timing (animValue, {
toValue : 1 ,
duration : 1000 ,
useNativeDriver : true , // Use native driver
}). start ();
// Change text separately after animation (no conflict with animation properties)
setTimeout (() => {
setText ( ' Updated Text ' );
}, 1000 );
};
return (
< View >
< Animated.View
style ={ {
opacity : animValue,
transform : [{ scale : animValue }],
} }
/>
< Button title = " Bad Animation (Layout Jank) " onPress ={ badAnimation } />
< Button title = " Good Animation (No Layout Jank) " onPress ={ goodAnimation } />
</ View >
);
};