001package react4j.internal; 002 003import arez.Arez; 004import arez.Disposable; 005import arez.annotations.ArezComponent; 006import javax.annotation.Nullable; 007import zemeckis.Zemeckis; 008 009/** 010 * Utilities for interacting with the Arez scheduler. 011 */ 012public final class SchedulerUtil 013{ 014 /** 015 * A non-null lock that will be released in the next micro-task which will schedule any renders required. 016 */ 017 @Nullable 018 private static Disposable c_schedulerLock; 019 020 private SchedulerUtil() 021 { 022 } 023 024 /** 025 * The first time an react4j view is rendered it will lock the Arez scheduler and release 026 * the lock in the micro-task immediately following the task that prompted the render. If this 027 * is not done it is possible that Arez can re-trigger a view render when the scheduler is 028 * triggered after the tracked render completes but before the render method has returned to the 029 * react runtime. This results in error message from react as a setState()/forceRender() was invoked 030 * while still within a render() method. 031 * 032 * <p>NOTE: While render methods are read-only transactions, they can un-observe components with 033 * {@link ArezComponent#disposeOnDeactivate()} set to <code>true</code> that would result in the 034 * arez component being disposed and triggering an update that would mark particular React 035 * view/Observers as STALE and trigger a re-render of that view.</p> 036 */ 037 public static void pauseUntilRenderLoopComplete() 038 { 039 if ( null == c_schedulerLock ) 040 { 041 c_schedulerLock = Arez.context().pauseScheduler(); 042 // schedule immediately after this call stack pops. 043 Zemeckis.microTask( () -> { 044 c_schedulerLock.dispose(); 045 c_schedulerLock = null; 046 } ); 047 } 048 } 049}