ReactLynx follows React's programming model, but leverages the dual-threaded runtimes provided by Lynx to achieve better performance and user experience through its own idioms (or rules).
When the component <HelloComponent />
is rendered, you will see "Hello" printed twice in the Console.
This happens because the code runs on two threads: main thread and background thread, which are part of Lynx's dual-threaded runtime.
However, not all code can be executed on both threads.
Consider the following example that adds a listener to GlobalEventEmitter
:
When the component <EventListenerComponent />
is rendered, you will see a "not a function" error. This occurs because while Lynx renders this component on both threads, lynx.getJSModule('GlobalEventEmitter')
cannot be executed on the main thread.
The reason relates to Lynx's dual-threaded runtime architecture, where the code for <EventListenerComponent />
executes on both threads:
On the background thread:
The lynx.getJSModule
function is available as part of the Lynx GlobalEventEmitter
API. Therefore, executing lynx.getJSModule('GlobalEventEmitter')
works without issues.
On the main thread:
The getJSModule
function does not exist on the main thread. Thus, when this code executes on the main thread, lynx.getJSModule
is evaluated as undefined
, leading to the "not a function" error.
Typically, side effects unrelated to rendering cannot be executed on the main thread, such as data updates, event listeners, timers, and network requests. Executing these side effects on the main thread will result in runtime errors. We call code that only executes on the background thread background only code. By marking background only code, we help the compiler optimize code and prevent these side effects from executing on the main thread.
There are three key rules for background only code:
Lynx considers code that meets any of these conditions as background only:
bindtap
/ catchtap
)useEffect
/ useLayoutEffect
)ref
prop'background only'
directiveimport 'background-only'
directiveFor example, all these functions with console.log
in the following example are considered background only. These functions will neither be bundled into main thread code nor executed on the main thread.
Following this rule, we can see that the earlier <EventListenerComponent />
should move its GlobalEventEmitter
usage into useEffect
.
This ensures this code only runs on the background thread where the GlobalEventEmitter
API is available:
When dependencies are involved, things become more complicated. Simply put, background only code can only be called by other background only code.
Usually, event callbacks of elements are considered background only. handleTap
doesn't need the 'background only'
directive but will be considered background only code because it's only used as a handler in the bindtap
event.
The backgroundOnly
function will also be considered background only because it's only called in the useEffect
callback.
Due to limitations in compiler analysis capabilities and compile-time performance, there are some exceptions to this rule. We will work to address these issues in future versions.
When event handlers are passed as props, you must add the 'background only'
directive. Otherwise, the compiler cannot identify handleTap
as background only code and will include it in the main thread bundle:
When using custom Hooks, you must add the 'background only'
directive. Otherwise, backgroundOnly
will be treated as non-background only code and included in the main thread bundle.
This occurs because the compiler cannot determine if the callback of useMount
is only used in background only code.
Just as some code (like GlobalEventEmitter
) only works on the background thread, there is also code that can only be executed on the main thread.
Main Thread Script (MTS) is script executed on the main thread.
For more details about MTS, including usage examples and best practices for handling animations and gestures on the main thread, please refer to Main Thread Script.
Lynx Engine also provides low-level APIs called Element PAPI.
Usually, Element PAPI calls are compiled by ReactLynx and you should not need to write any Element PAPI code.