Welcome to the twelfth edition of React Native Weekly. This is the retrospect of week 23 of 2021.
Reach out to me via Twitter if you have any feedback, and don’t forget to subscribe!
Clean Ups & Improvements
This week there were a few clean ups and improvements done to the codebase, like:
- Improvements over the Flow Definitions 1 and 2.
- Deletion of unused Text Injection
- Improvement to a C++ large array allocation
- Fix Gradle warning to use maven-publish
- Bump Gradle to 6.9 which supports Apple Silicon.
- Bump buildToolsVersion to 30.0.2
Remove JCenter [Android]
This commit removes jcenter
.
jcenter is read-only now, and newer versions of dependencies will be published to either MavenCentral or Jitpack. This PR removes jcenter to avoid future issues, then uses MavenCentral and Jitpack as replacement. Current flipper depends on Stetho version that is not available on MavenCentral, so had to exclude and bump the version.
Delete eager initialization of Fabric classes
David Vacca dvacca@fb.com ran an experiment on the Marketplace app to see the impact of eagerly initializing Fabric classes as follows:
- // TODO T31905686: eager load Fabric classes, this is temporary and it will be removed
- // in the near future
- private static void loadClasses() {
- EventBeatManager.class.getClass();
- EventEmitterWrapper.class.getClass();
- FabricEventEmitter.class.getClass();
- DispatchCommandMountItem.class.getClass();
- DispatchIntCommandMountItem.class.getClass();
- DispatchStringCommandMountItem.class.getClass();
- IntBufferBatchMountItem.class.getClass();
- MountItem.class.getClass();
- PreAllocateViewMountItem.class.getClass();
- SendAccessibilityEvent.class.getClass();
- LayoutMetricsConversions.class.getClass();
- MountingManager.class.getClass();
- MountItemDispatcher.class.getClass();
- SurfaceMountingManager.class.getClass();
- Binding.class.getClass();
- ComponentFactory.class.getClass();
- CoreComponentsRegistry.class.getClass();
- FabricComponents.class.getClass();
- FabricSoLoader.class.getClass();
- FabricUIManager.class.getClass();
- GuardedFrameCallback.class.getClass();
- StateWrapper.class.getClass();
- StateWrapperImpl.class.getClass();
- SurfaceHandler.class.getClass();
- SurfaceHandlerBinding.class.getClass();
- BatchEventDispatchedListener.class.getClass();
- ReactNativeConfig.class.getClass();
- ReadableMapBuffer.class.getClass();
- }
The result was neutral and caused a regression thus he removed in this commit
Ship bridge RuntimeExecutor JSIExecutor flushing
For those who are new to flushing and React Native’s event system - React Native’s event system that handles calls into native and JavaScript uses a queue mechanism, in order to empty the queue it flushes it, which means it empties whatever is in the queue and handles the calls.
According to Ramanpreet Nara ramanpreet@fb.com>, the RuntimeExecutor
that Fabric gets wasn’t calling JSIExecutor::flush()
as frequently as it was on the legacy NativeModule
system, it is supposed to call flush
on every call. His commit changed this so Flush NativeModule calls with Fabric on Android on every Native - JS call
diff --git a/ReactAndroid/src/main/jni/react/jni/CatalystInstanceImpl.h b/ReactAndroid/src/main/jni/react/jni/CatalystInstanceImpl.h
index fff64a0a79d..c40a691232c 100644
--- a/ReactAndroid/src/main/jni/react/jni/CatalystInstanceImpl.h
+++ b/ReactAndroid/src/main/jni/react/jni/CatalystInstanceImpl.h
...
+RuntimeExecutor Instance::getRuntimeExecutor() {
std::weak_ptr<NativeToJsBridge> weakNativeToJsBridge = nativeToJsBridge_;
auto runtimeExecutor =
- [weakNativeToJsBridge,
- shouldFlush](std::function<void(jsi::Runtime & runtime)> &&callback) {
+ [weakNativeToJsBridge](
+ std::function<void(jsi::Runtime & runtime)> &&callback) {
if (auto strongNativeToJsBridge = weakNativeToJsBridge.lock()) {
strongNativeToJsBridge->runOnExecutorQueue(
- [callback = std::move(callback),
- shouldFlush](JSExecutor *executor) {
+ [callback = std::move(callback)](JSExecutor *executor) {
jsi::Runtime *runtime =
(jsi::Runtime *)executor->getJavaScriptContext();
try {
callback(*runtime);
- if (shouldFlush) {
- executor->flush();
- }
+ executor->flush();
} catch (jsi::JSError &originalError) {
handleJSError(*runtime, originalError, true);
}
Custom NSURLSession configuration (#27701) [External Contribution]
Stemmed from react native community discussion
[iOS] [Added] - Allow for configuring the NSURLSessionConfiguration
Implement a C function
RCTSetCustomNSURLSessionConfigurationProvider
which gives the app programmer the ability to provide a block which provides an NSURLSessionConfiguration that will be used for all HTTP requests instead of the default configuration. The provided block will be called when the session configuration is needed.
Experimental Animated API?
Tim Yung yungsters@fb.com is experimenting with a new createAnimatedComponent
that is compatible with concurrent rendering. Let’s wait and see what this results in. See commit 1 and 2
This work results in a custom hook.
Introduce useAnimatedProps
hook
Creates a useAnimatedProps
hook that is compatible with concurrent mode, and uses this in createAnimatedComponent_EXPERIMENTAL
.
Note that this is all experimental.
Create useMergeRefs
Utility
Creates useMergeRefs
which will be used by components in React Native.
+/**
+ * Constructs a new ref that forwards new values to each of the given refs. The
+ * given refs will always be invoked in the order that they are supplied.
+ *
+ * WARNING: A known problem of merging refs using this approach is that if any
+ * of the given refs change, the returned callback ref will also be changed. If
+ * the returned callback ref is supplied as a `ref` to a React element, this may
+ * lead to problems with the given refs being invoked more times than desired.
+ */
Create useRefEffect
Utility
+/**
+ * Constructs a callback ref that provides similar semantics as `useEffect`. The
+ * supplied `effect` callback will be called with non-null component instances.
+ * The `effect` callback can also optionally return a cleanup function.
+ *
+ * When a component is updated or unmounted, the cleanup function is called. The
+ * `effect` callback will then be called again, if applicable.
+ *
+ * When a new `effect` callback is supplied, the previously returned cleanup
+ * function will be called before the new `effect` callback is called with the
+ * same instance.
+ *
+ * WARNING: The `effect` callback should be stable (e.g. using `useCallback`).
+ */
+export default function useRefEffect<TInstance>(
+ effect: TInstance => (() => void) | void,
+): CallbackRef<TInstance | null> {
+ const cleanupRef = useRef<(() => void) | void>(undefined);
+ return useCallback(
+ instance => {
+ if (cleanupRef.current) {
+ cleanupRef.current();
+ cleanupRef.current = undefined;
+ }
+ if (instance != null) {
+ cleanupRef.current = effect(instance);
+ }
+ },
+ [effect],
+ );
+}
Tim Yung yungsters@fb.com has been busy 😁
Create swipeable card demo
This verifies interaction between PanResponder and ScrollView with JSResponderHandler. Also showcases how to create a swipeable card with scrollable content.
function SwipeableCard() {
const movementX = React.useRef(new Animated.Value(0)).current;
const panResponder = React.useRef(
PanResponder.create({
onMoveShouldSetPanResponderCapture: (e, gestureState) => {
const {dx} = gestureState;
return Math.abs(dx) > 5;
},
onPanResponderMove: (e, gestureState) => {
Animated.event([null, {dx: movementX}], {
useNativeDriver: false,
})(e, gestureState);
},
onPanResponderEnd: (e, gestureState) => {
Animated.timing(movementX, {
toValue: 0,
useNativeDriver: true,
}).start();
},
}),
).current;
const {width} = useWindowDimensions();
const rotation = movementX.interpolate({
inputRange: [-width / 2, 0, width / 2],
outputRange: (['-5deg', '0deg', '5deg']: $ReadOnlyArray<string>),
extrapolate: 'clamp',
});
return (
<View style={styles.container}>
<Animated.View
{...panResponder.panHandlers}
style={{
transform: [{translateX: movementX}, {rotateZ: rotation}],
flex: 1,
}}>
<Card />
</Animated.View>
</View>
);
}
Ship JS Responder in Fabric
Comment from Joshua Gross joshuagross@fb.com:
setJSResponder/clearJSResponder have been in use in prod for a while and are stable. Ship them in code.
Bug fix: TextInput content is reset when emoji is entered at the max length [iOS]
When maxLength is defined in TextInput, if the last character at max length is an emoji, the content of the input is cleared:
Directory.now.sh
I recently saw a tweet from Leandro Favre about directory.now.sh - a quick font search for expo-google-fonts. He also has one for icons
React Native 0.64.2 is out
Moti can power mount/unmount animations
You can typically do this imperatively by having a separate state for the visibility of your component in a parent component and only unmounting after your animation ran, however this usually gets messy.
Moti handles it for you and Fernando made a tweet with an Expo Snack showing you how you can do it without Moti.
You can hide YellowBox/LogBox with warnings
Daniel Koprowski shared a tweet on how he hides the YellowBox/LogBox logs when demoing his apps.
Space Game With React Native
Found this gem from friggitydingo AKA @BenScottSteer on Twitter, a Space Game built with React Native, looks great and there are two videos of how he is building it. Pretty cool!
Building a Custom Spritesheet Player in React Native
Making a Reusable Modal for React Native Apps
Flutter Versus React Native Series
I was very happy to see React Newsletter by UI.dev tweet the series I’m writing with G2i.
react-native-voice/voice has full Expo intergration support
react-native-voice/voice now has a config plugin and supports expo prebuild, expo run commands, eas build, and the dev client beta.
That Is It!
That’s it for this week. If you want to see more checkout previous week’s posts here. Subscribe to get notified when new posts are out = )