001package react4j.dom;
002
003import arez.Arez;
004import arez.ArezContext;
005import javax.annotation.Nonnull;
006import javax.annotation.Nullable;
007import jsinterop.annotations.JsFunction;
008import jsinterop.annotations.JsMethod;
009import jsinterop.annotations.JsOverlay;
010import jsinterop.annotations.JsPackage;
011import jsinterop.annotations.JsType;
012import react4j.React;
013import react4j.ReactElement;
014import react4j.ReactNode;
015
016/**
017 * Core interface into React DOM library.
018 */
019@JsType( isNative = true, namespace = JsPackage.GLOBAL )
020public class ReactDOM
021{
022  private ReactDOM()
023  {
024  }
025
026  /**
027   * Interface for performing an action inside batch.
028   */
029  @FunctionalInterface
030  @JsFunction
031  public interface BatchedUpdatesFn
032  {
033    /**
034     * Perform action while batching react changes.
035     *
036     * @throws Throwable if an error occurred.
037     */
038    void call()
039      throws Throwable;
040  }
041
042  /**
043   * Interface for performing an action on render complete.
044   */
045  @FunctionalInterface
046  @JsFunction
047  public interface RenderCallbackFn
048  {
049    /**
050     * Perform action on render complete.
051     */
052    void call();
053  }
054
055  @JsOverlay
056  @Nonnull
057  public static ReactRoot createRoot( @Nonnull final Object container )
058  {
059    return unstable_createRoot( container );
060  }
061
062  @Nonnull
063  private static native ReactRoot unstable_createRoot( @Nonnull Object container );
064
065  /**
066   * Portals provide a first-class way to render children into a DOM node that exists outside the DOM hierarchy
067   * of the parent component.
068   *
069   * <p>Even though a portal can be anywhere in the DOM tree, it behaves like a normal React child in every
070   * other way. Features like context work exactly the same regardless of whether the child is a portal, as
071   * the portal still exists in the React tree regardless of position in the DOM tree.</p>
072   *
073   * <p>This includes event bubbling. An event fired from inside a portal will propagate to ancestors in
074   * the containing React tree, even if those elements are not ancestors in the DOM tree.</p>
075   *
076   * @param children  the react node to render.
077   * @param container the DOM element to render into.
078   * @return the new portal.
079   */
080  public static native ReactPortal createPortal( @Nonnull ReactNode children, @Nonnull Object container );
081
082  /**
083   * Render a React element into the DOM in the supplied container.
084   *
085   * <p>If the React element was previously rendered into container, this will perform an update on it and only
086   * mutate the DOM as necessary to reflect the latest React element.</p>
087   *
088   * <p>If the optional callback is provided, it will be executed after the component is rendered or updated.</p>
089   *
090   * @param node      the react node to render.
091   * @param container the DOM element to render into.
092   * @param onUpdate  the callback invoked when rendering is complete.
093   * @return a reference to the created React Component, DOM Node, Portal or null (stateless components).
094   */
095  @Nullable
096  @JsOverlay
097  public static Object render( @Nonnull final ReactNode node,
098                               @Nonnull final Object container,
099                               @Nullable final RenderCallbackFn onUpdate )
100  {
101    return _render( React.shouldCheckInvariants() ? ReactElement.createStrictMode( node ) : node, container, onUpdate );
102  }
103
104  @Nullable
105  @JsMethod( name = "render" )
106  private static native Object _render( @Nonnull ReactNode node,
107                                        @Nonnull Object container,
108                                        @Nullable RenderCallbackFn onUpdate );
109
110  /**
111   * Render a React element into the DOM in the supplied container.
112   *
113   * <p>If the React element was previously rendered into container, this will perform an update on it and only
114   * mutate the DOM as necessary to reflect the latest React element.</p>
115   *
116   * @param node      the react node to render.
117   * @param container the DOM element to render into.
118   * @return a reference to the created React Component, DOM Node, Portal or null (stateless components).
119   * @see #render(ReactNode, Object, RenderCallbackFn)
120   */
121  @Nullable
122  @JsOverlay
123  public static Object render( @Nonnull ReactNode node, @Nonnull Object container )
124  {
125    return render( node, container, null );
126  }
127
128  /**
129   * Remove a mounted React component from the DOM and clean up its event handlers and state. If
130   * no component was mounted in the container, calling this function does nothing.
131   *
132   * @param container the DOM container containing the react component to unmount
133   * @return true if a component was unmounted and false if there was no component to unmount.
134   */
135  public static native boolean unmountComponentAtNode( @Nonnull Object container );
136
137  /**
138   * Batch all state updates within the action.
139   * This is currently an unstable API within the React 16, mostly because it is only useful when called
140   * outside an event handler (i.e. from network code) and because it is likely to be enabled by default
141   * in a later version of React.
142   *
143   * @param action the action where all state updates are batched.
144   */
145  @JsOverlay
146  public static void batchedUpdates( @Nonnull final BatchedUpdatesFn action )
147  {
148    unstable_batchedUpdates( action );
149  }
150
151  /**
152   * Register an task interceptor on the current Arez context that ensures any view updates are batched.
153   */
154  @JsOverlay
155  public static void registerBatchedArezTaskInterceptor()
156  {
157    registerBatchedArezTaskInterceptor( Arez.context() );
158  }
159
160  /**
161   * Register an task interceptor that ensures any view updates are batched.
162   *
163   * @param context the context to add interceptor to.
164   */
165  @JsOverlay
166  public static void registerBatchedArezTaskInterceptor( @Nonnull final ArezContext context )
167  {
168    context.setTaskInterceptor( new BatchingTaskInterceptor() );
169  }
170
171  /**
172   * The native method with the unstable prefix.
173   *
174   * @param action the action where all state updates are batched.
175   */
176  @JsMethod
177  private static native void unstable_batchedUpdates( @Nonnull BatchedUpdatesFn action );
178}