Sync External Store for Consistent UI Across Threads
Use React's useSyncExternalStore hook to bridge UI state between the JavaScript thread and Reanimated worklets, eliminating flicker and ensuring deterministic updates.
Insight
React 18 introduced useSyncExternalStore to provide a reliable subscription model for external data sources. In React Native, you can exploit this hook to keep UI components in sync with Reanimated worklets that run on the UI thread. By exposing a tiny store that both the JS thread and the worklet can read, you avoid the classic "state lag" when animating based on async data.
Example
// store.ts
import { EventEmitter } from 'events';
export const uiStore = new EventEmitter();
export let value = 0;
export const setValue = (v: number) => {
value = v;
uiStore.emit('change');
};
// Component.tsx
import { useSyncExternalStore } from 'react';
import { uiStore, value } from './store';
import { useSharedValue, useDerivedValue, runOnJS } from 'react-native-reanimated';
export default function Counter() {
const count = useSyncExternalStore(
(cb) => uiStore.on('change', cb),
() => value
);
const sv = useSharedValue(count);
sv.value = count; // UI thread sees the latest value instantly
return <Text>{sv.value}</Text>;
}
Takeaway
Wrap any mutable value in a tiny EventEmitter‑based store and read it with useSyncExternalStore. This guarantees that both the JS thread and Reanimated worklets see the same snapshot, eliminating UI jitter without pulling in a full state‑management library.