All Downloads are FREE. Search and download functionalities are using the official Maven repository.

crystal.react.hooks.UseAsyncEffect.scala Maven / Gradle / Ivy

There is a newer version: 0.47.1
Show newest version
// Copyright (c) 2016-2023 Association of Universities for Research in Astronomy, Inc. (AURA)
// For license information see LICENSE or https://opensource.org/licenses/BSD-3-Clause

package crystal.react.hooks

import crystal.react.*
import crystal.react.reuse.*
import japgolly.scalajs.react.*
import japgolly.scalajs.react.hooks.CustomHook
import japgolly.scalajs.react.util.DefaultEffects.Async as DefaultA

object UseAsyncEffect {

  /**
   * Run async effect and cancel previously running instances, thus avoiding race conditions. Allows
   * returning a cleanup effect.
   */
  final def useAsyncEffectWithDeps[G, D: Reusability](deps: => D)(effect: D => G)(using
    G: EffectWithCleanup[G, DefaultA]
  ): HookResult[Unit] =
    // hookBuilder(WithDeps(deps, effect))
    useSingleEffect.flatMap: dispatcher =>
      useEffectWithDeps(deps): deps =>
        dispatcher.submit(effect(deps).normalize)

  /**
   * Run async effect and cancel previously running instances, thus avoiding race conditions. Allows
   * returning a cleanup effect.
   */
  final inline def useAsyncEffect[G](effect: => G)(using
    G: EffectWithCleanup[G, DefaultA]
  ): HookResult[Unit] =
    useAsyncEffectWithDeps(NeverReuse)((_: Reuse[Unit]) => effect)

  /**
   * Run async effect and cancel previously running instances, thus avoiding race conditions. Allows
   * returning a cleanup effect.
   */
  final inline def useAsyncEffectOnMount[G](effect: => G)(using
    G: EffectWithCleanup[G, DefaultA]
  ): HookResult[Unit] = // () has Reusability = always.
    useAsyncEffectWithDeps(())((_: Unit) => effect)

  // *** The rest is to support builder-style hooks *** //

  private def hook[G, D: Reusability](using
    EffectWithCleanup[G, DefaultA]
  ): CustomHook[WithDeps[D, G], Unit] =
    CustomHook.fromHookResult(input => useAsyncEffectWithDeps(input.deps)(input.fromDeps))

  object HooksApiExt {
    sealed class Primary[Ctx, Step <: HooksApi.AbstractStep](api: HooksApi.Primary[Ctx, Step]) {

      /**
       * Run async effect and cancel previously running instances, thus avoiding race conditions.
       * Allows returning a cleanup effect.
       */
      final def useAsyncEffectWithDeps[G, D: Reusability](deps: => D)(effect: D => G)(using
        step: Step,
        G:    EffectWithCleanup[G, DefaultA]
      ): step.Self =
        useAsyncEffectWithDepsBy(_ => deps)(_ => effect)

      /**
       * Run async effect and cancel previously running instances, thus avoiding race conditions.
       * Allows returning a cleanup effect.
       */
      final def useAsyncEffect[G](effect: G)(using
        step: Step,
        G:    EffectWithCleanup[G, DefaultA]
      ): step.Self =
        useAsyncEffectBy(_ => effect)

      /**
       * Run async effect and cancel previously running instances, thus avoiding race conditions.
       * Allows returning a cleanup effect.
       */
      final def useAsyncEffectOnMount[G](effect: G)(using
        step: Step,
        G:    EffectWithCleanup[G, DefaultA]
      ): step.Self =
        useAsyncEffectOnMountBy(_ => effect)

      /**
       * Run async effect and cancel previously running instances, thus avoiding race conditions.
       * Allows returning a cleanup effect.
       */
      final def useAsyncEffectWithDepsBy[G, D: Reusability](deps: Ctx => D)(effect: Ctx => D => G)(
        using
        step: Step,
        G:    EffectWithCleanup[G, DefaultA]
      ): step.Self =
        api.customBy { ctx =>
          val hookInstance = hook[G, D]
          hookInstance(WithDeps(deps(ctx), effect(ctx)))
        }

      /**
       * Run async effect and cancel previously running instances, thus avoiding race conditions.
       * Allows returning a cleanup effect.
       */
      final def useAsyncEffectBy[G](effect: Ctx => G)(using
        step: Step,
        G:    EffectWithCleanup[G, DefaultA]
      ): step.Self =
        useAsyncEffectWithDepsBy(_ => NeverReuse)(ctx => (_: Reuse[Unit]) => effect(ctx))

      /**
       * Run async effect and cancel previously running instances, thus avoiding race conditions.
       * Allows returning a cleanup effect.
       */
      final def useAsyncEffectOnMountBy[G](effect: Ctx => G)(using
        step: Step,
        G:    EffectWithCleanup[G, DefaultA]
      ): step.Self = // () has Reusability = always.
        useAsyncEffectWithDepsBy(_ => ())(ctx => (_: Unit) => effect(ctx))
    }

    final class Secondary[Ctx, CtxFn[_], Step <: HooksApi.SubsequentStep[Ctx, CtxFn]](
      api: HooksApi.Secondary[Ctx, CtxFn, Step]
    ) extends Primary[Ctx, Step](api) {

      /**
       * Run async effect and cancel previously running instances, thus avoiding race conditions.
       * Allows returning a cleanup effect.
       */
      def useAsyncEffectWithDepsBy[G, D: Reusability](deps: CtxFn[D])(effect: CtxFn[D => G])(using
        step: Step,
        G:    EffectWithCleanup[G, DefaultA]
      ): step.Self =
        useAsyncEffectWithDepsBy(step.squash(deps)(_))(step.squash(effect)(_))

      /**
       * Run async effect and cancel previously running instances, thus avoiding race conditions.
       * Allows returning a cleanup effect.
       */
      def useAsyncEffectBy[G](
        effect: CtxFn[G]
      )(using step: Step, G: EffectWithCleanup[G, DefaultA]): step.Self =
        useAsyncEffectBy(step.squash(effect)(_))

      /**
       * Run async effect and cancel previously running instances, thus avoiding race conditions.
       * Allows returning a cleanup effect.
       */
      def useAsyncEffectOnMountBy[G](effect: CtxFn[G])(using
        step: Step,
        G:    EffectWithCleanup[G, DefaultA]
      ): step.Self =
        useAsyncEffectOnMountBy(step.squash(effect)(_))
    }
  }

  protected trait HooksApiExt {
    import HooksApiExt.*

    implicit def hooksExtAsyncEffect1[Ctx, Step <: HooksApi.AbstractStep](
      api: HooksApi.Primary[Ctx, Step]
    ): Primary[Ctx, Step] =
      new Primary(api)

    implicit def hooksExtAsyncEffect2[Ctx, CtxFn[_], Step <: HooksApi.SubsequentStep[Ctx, CtxFn]](
      api: HooksApi.Secondary[Ctx, CtxFn, Step]
    ): Secondary[Ctx, CtxFn, Step] =
      new Secondary(api)
  }

  object syntax extends HooksApiExt
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy