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

japgolly.scalajs.react.extra.ReusabilityOverlay.scala Maven / Gradle / Ivy

There is a newer version: 3.0.0-beta12
Show newest version
package japgolly.scalajs.react.extra

import japgolly.scalajs.react._
import japgolly.scalajs.react.internal.Box
import japgolly.scalajs.react.util.DefaultEffects
import japgolly.scalajs.react.util.Effect.Sync
import scala.scalajs.js

/**
 * Heavily inspired by https://github.com/redsunsoft/react-render-visualizer
 */
object ReusabilityOverlay {
  type Comp = ScalaComponent.MountedImpure[Any, Any, Any]

  private val key = "reusabilityOverlay"

  /** When you're in dev-mode (i.e. `fastOptJS`), this overrides [[Reusability.shouldComponentUpdate]] to use overlays.
    */
  @inline def overrideGloballyInDev(): Unit =
    DefaultReusabilityOverlay.overrideGloballyInDev()

  def install[P: Reusability, C <: Children, S: Reusability, B, U <: UpdateSnapshot]: ScalaComponent.Config[P, C, S, B, U, U] = {
    import DefaultEffects.Sync
    install(DefaultReusabilityOverlay.defaults)
  }

  def install[F[_]: Sync, P: Reusability, C <: Children, S: Reusability, B, U <: UpdateSnapshot]
      (newOverlay: ScalaComponent.MountedImpure[P, S, B] => ReusabilityOverlay[F]): ScalaComponent.Config[P, C, S, B, U, U] = {

    // Store the overlay stats on each instance
    def get(raw: ScalaComponent.RawMounted[P, S, B]): ReusabilityOverlay[F] = {
      def $ = raw.asInstanceOf[js.Dynamic]
      $.selectDynamic(key).asInstanceOf[js.UndefOr[Box[ReusabilityOverlay[F]]]].fold {
        val o = newOverlay(ScalaComponent.mountRaw(raw))
        $.updateDynamic(key)(Box(o))
        o
      }(_.unbox)
    }

    Reusability.shouldComponentUpdateAnd[F, P, C, S, B, U] { r =>
      val overlay = get(r.mounted.js.raw)
      if (r.update) {
        def fmt(update: Boolean, name: String, va: Any, vb: Any) =
          if (!update)
            ""
          else {
            var a = va.toString
            var b = vb.toString
            if (a.contains(' ') || b.contains(' ')) {
              a = "【" + a + "】"
              b = "【" + b + "】"
            }
            if (a.contains('\n') || a.length > 50 || b.length > 50)
              s"$name update:\n  BEFORE: $a\n   AFTER: $b"
            else
              s"$name update: $a ⇒ $b"
          }
        val sep = if (r.updateProps && r.updateState) "\n" else ""
        val reason = fmt(r.updateProps, "Props", r.currentProps, r.nextProps) + sep +
                     fmt(r.updateState, "State", r.currentState, r.nextState)
        overlay logBad reason
      }
      else
        overlay.logGood
    } andThen (_
      .componentDidMount(i => get(i.raw).onMount)
      .componentWillUnmount(i => get(i.raw).onUnmount)
    )
  }
}

trait ReusabilityOverlay[F[_]] {
  def onMount  : F[Unit]
  def onUnmount: F[Unit]
  val logGood  : F[Unit]
  def logBad(reason: String): F[Unit]
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy