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

japgolly.scalajs.react.Reusability.scala Maven / Gradle / Ivy

The newest version!
package japgolly.scalajs.react

import japgolly.scalajs.react.internal.Box
import japgolly.scalajs.react.internal.CoreGeneral._
import japgolly.scalajs.react.util.Effect.Sync
import japgolly.scalajs.react.util.{DefaultEffects, OptionLike}
import java.time._
import java.util.{Date, UUID}
import java.{math => jm}
import org.scalajs.dom.console
import scala.annotation.tailrec
import scala.collection.immutable.ArraySeq
import scala.concurrent.{duration => scd}
import scala.reflect.ClassTag
import scala.scalajs.js.timers.{SetIntervalHandle, SetTimeoutHandle}
import scala.scalajs.js.{Date => JsDate, |}
import scala.{math => sm}

/**
 * Tests whether one instance can be used in place of another.
 * Used mostly to compare properties and state of a component to avoid unnecessary updates.
 *
 * If you imagine a class with 8 fields, equality would compare all 8 fields where as this would typically just compare
 * the ID field, the update-date, or the revision number.
 * You might think of this as a very quick version of equality.
 *
 * Don't miss `Reusability.shouldComponentUpdate` which can be applied to a component via
 * `ScalaComponent.build#configure`.
 *
 * @since 0.9.0
 */
final class Reusability[A](val test: (A, A) => Boolean) extends AnyVal {

  @inline def updateNeeded(x: A, y: A): Boolean =
    !test(x, y)

  def contramap[B](f: B => A): Reusability[B] =
    new Reusability((x, y) => test(f(x), f(y)))

  def narrow[B <: A]: Reusability[B] =
    new Reusability[B](test)

  def unsafeWiden[B >: A]: Reusability[B] =
    unsafeSubst[B]

  def unsafeSubst[B]: Reusability[B] =
    new Reusability[B](test.asInstanceOf[(B, B) => Boolean])

  def testNot: (A, A) => Boolean =
    !test(_, _)

  def ||[B <: A](tryNext: Reusability[B]): Reusability[B] =
    Reusability[B]((x, y) => test(x, y) || tryNext.test(x, y))

  def &&[B <: A](tryNext: Reusability[B]): Reusability[B] =
    Reusability[B]((x, y) => test(x, y) && tryNext.test(x, y))

  def reusable(a: A)(implicit c: ClassTag[A]): Reusable[A] =
    Reusable.explicitly(a)(this)(c)

  def logNonReusable: Reusability[A] = logNonReusable()

  def logNonReusable(show: A => String = _.toString,
                     log : String => Unit = console.warn(_),
                     title: String = "Non-reusability:",
                     fmt : (String, => String, => String) => String = (t, x, y) => s"$t\n- $x\n- $y"): Reusability[A] =
    Reusability { (a, b) =>
      val r = test(a, b)
      if (!r)
        log(fmt(title, show(a), show(b)))
      r
    }
}

object Reusability extends ReusabilityMacros with ScalaVersionSpecificReusability {

  @inline def apply[A](f: (A, A) => Boolean): Reusability[A] =
    new Reusability(f)

  def suspend[A](f: => Reusability[A]): Reusability[A] =
    new Reusability((a, b) => f.test(a, b))

  @deprecated("Use Reusability.suspend", "2.0.0")
  def byName[A](f: => Reusability[A]): Reusability[A] =
    suspend(f)

  private[this] val alwaysInstance: Reusability[Any] =
    apply((_, _) => true)

  def always[A]: Reusability[A] =
    alwaysInstance.asInstanceOf[Reusability[A]]

  private[this] val neverInstance: Reusability[Any] =
    apply((_, _) => false)

  def never[A]: Reusability[A] =
    neverInstance.asInstanceOf[Reusability[A]]

  def const[A](r: Boolean): Reusability[A] =
    if (r) always else never

  /** Compare by reference. Reuse if both values are the same instance. */
  def byRef[A <: AnyRef]: Reusability[A] =
    new Reusability((a, b) => a eq b)

  /** Compare using universal equality (Scala's == operator). */
  def by_==[A]: Reusability[A] =
    new Reusability((a, b) => a == b)

  /** Compare by reference and if different, compare using universal equality (Scala's == operator). */
  def byRefOr_==[A <: AnyRef]: Reusability[A] =
    byRef[A] || by_==[A]

  def by[A, B](f: A => B)(implicit r: Reusability[B]): Reusability[A] =
    r contramap f

  def byIterator[I[X] <: Iterable[X], A: Reusability]: Reusability[I[A]] =
    apply { (x, y) =>
      val i = x.iterator
      val j = y.iterator
      @tailrec
      def go: Boolean = {
        val hasNext = i.hasNext
        if (hasNext != j.hasNext)
          false
        else if (!hasNext)
          true
        else if (i.next() ~/~ j.next())
          false
        else
          go
      }
      go
    }

  def indexedSeq[S[X] <: IndexedSeq[X], A: Reusability]: Reusability[S[A]] =
    apply((x, y) =>
      (x.length == y.length) && x.indices.forall(i => x(i) ~=~ y(i)))

  def double(tolerance: Double): Reusability[Double] =
    apply((x, y) => (x - y).abs <= tolerance)

  def float(tolerance: Float): Reusability[Float] =
    apply((x, y) => (x - y).abs <= tolerance)

  def javaBigDecimal(tolerance: Double): Reusability[jm.BigDecimal] =
    javaBigDecimal(new jm.BigDecimal(tolerance))

  def javaBigDecimal(tolerance: jm.BigDecimal): Reusability[jm.BigDecimal] =
    apply((x, y) => x.subtract(y).abs.compareTo(tolerance) <= 0)

  def scalaBigDecimal(tolerance: sm.BigDecimal): Reusability[sm.BigDecimal] =
    apply((x, y) => (x - y).abs.compareTo(tolerance) <= 0)

  def byJavaDuration[A](dur: (A, A) => Duration, tolerance: Duration): Reusability[A] =
    apply { (x, y) =>
      val d = dur(x, y).abs
      d.compareTo(tolerance) <= 0
    }

  def javaDuration(tolerance: Duration): Reusability[Duration] =
    byRef || byJavaDuration[Duration](_ minus _, tolerance)

  def instant(tolerance: Duration): Reusability[Instant] =
    byRef || byJavaDuration[Instant](Duration.between, tolerance)

  def localDateTime(tolerance: Duration): Reusability[LocalDateTime] =
    byRef || byJavaDuration[LocalDateTime](Duration.between, tolerance)

  def localDate(tolerance: Duration): Reusability[LocalDate] =
    byRef || byJavaDuration[LocalDate](Duration.between, tolerance)

  def offsetDateTime(tolerance: Duration): Reusability[OffsetDateTime] =
    byRef || byJavaDuration[OffsetDateTime](Duration.between, tolerance)

  def offsetTime(tolerance: Duration): Reusability[OffsetTime] =
    byRef || byJavaDuration[OffsetTime](Duration.between, tolerance)

  def zonedDateTime(tolerance: Duration): Reusability[ZonedDateTime] =
    byRef || byJavaDuration[ZonedDateTime](Duration.between, tolerance)

  def byScalaDuration[A](dur: (A, A) => scd.Duration, tolerance: scd.Duration): Reusability[A] =
    apply { (x, y) =>
      var d = dur(x, y)
      if (d.length < 0)
        d = scd.FiniteDuration(d.length.abs, d.unit)
      d <= tolerance
    }

  def scalaDuration(tolerance: scd.Duration): Reusability[scd.Duration] =
    byRef || byScalaDuration[scd.Duration](_ - _, tolerance)

  def finiteDuration(tolerance: scd.Duration): Reusability[scd.FiniteDuration] =
    scalaDuration(tolerance).narrow

  def deadline(tolerance: scd.Duration): Reusability[scd.Deadline] =
    finiteDuration(tolerance).contramap(_.time)

  /**
   * This is not implicit because the point of Reusability is to be fast, where as full comparison of all keys and
   * values in a map, is usually not desirable; in some cases it will probably even be faster just rerender and have
   * React determine that nothing has changed.
   *
   * Nonetheless, there are cases where a full comparison is desired and so use this as needed. `Reusability[K]` isn't
   * needed because its existence in the map (and thus universal equality) is all that's necessary.
   * Time is O(|m₁|+|m₂|).
   */
  def map[K, V](implicit rv: Reusability[V]): Reusability[Map[K, V]] =
    byRef[Map[K, V]] || apply((m, n) =>
      if (m.isEmpty)
        n.isEmpty
      else if (n.isEmpty)
        false
      else {
        var ok = true
        var msize = 0

        val mi = m.iterator
        while (ok && mi.hasNext) {
          val (k, v) = mi.next()
          msize += 1
          ok = n.get(k).exists(rv.test(v, _))
        }

        ok && msize == n.size
      }
    )

  /** Declare a type reusable when both values pass a given predicate. */
  def when[A](f: A => Boolean): Reusability[A] =
    apply((a, b) => f(a) && f(b))

  /** Declare a type reusable when both values fail a given predicate. */
  def unless[A](f: A => Boolean): Reusability[A] =
    when(!f(_))

  // -------------------------------------------------------------------------------------------------------------------
  // Implicit Instances

  // Prohibited:
  // ===========
  // Array  - it's mutable. Reusability & mutability are incompatible.
  // Stream - it's lazy. Reusability & non-strictness are incompatible.

  @inline implicit def unit   : Reusability[Unit   ] = always
  @inline implicit def boolean: Reusability[Boolean] = by_==
  @inline implicit def byte   : Reusability[Byte   ] = by_==
  @inline implicit def char   : Reusability[Char   ] = by_==
  @inline implicit def short  : Reusability[Short  ] = by_==
  @inline implicit def int    : Reusability[Int    ] = by_==
  @inline implicit def long   : Reusability[Long   ] = by_==
  @inline implicit def string : Reusability[String ] = by_==
  @inline implicit def date   : Reusability[Date   ] = by_==
  @inline implicit def uuid   : Reusability[UUID   ] = by_==

  implicit def jsDate: Reusability[JsDate] =
    apply((x, y) => x.getTime() == y.getTime())

  @inline implicit def option[A: Reusability]: Reusability[Option[A]] =
    optionLike

  implicit def optionLike[O[_], A](implicit o: OptionLike[O], r: Reusability[A]): Reusability[O[A]] =
    apply((x, y) =>
      o.fold(x, o isEmpty y)(xa =>
        o.fold(y, false)(ya =>
          xa ~=~ ya)))

  implicit def either[A: Reusability, B: Reusability]: Reusability[Either[A, B]] =
    apply((x, y) =>
      x.fold[Boolean](
        a => y.fold(a ~=~ _, _ => false),
        b => y.fold(_ => false, b ~=~ _)))

  implicit def list[A: Reusability]: Reusability[List[A]] =
    byRef[List[A]] || byIterator[List, A]

  implicit def vector[A: Reusability]: Reusability[Vector[A]] =
    byRef[Vector[A]] || indexedSeq[Vector, A]

  implicit def set[A]: Reusability[Set[A]] =
    byRefOr_== // universal equality must hold for Sets

  implicit def arraySeq[A: Reusability]: Reusability[ArraySeq[A]] =
    byRef[ArraySeq[A]] || indexedSeq[ArraySeq, A]

  implicit def box[A: Reusability]: Reusability[Box[A]] =
    by(_.unbox)

  implicit def range: Reusability[Range] =
    byRefOr_==

  implicit lazy val setIntervalHandle: Reusability[SetIntervalHandle] =
    by_==

  implicit lazy val setTimeoutHandle: Reusability[SetTimeoutHandle] =
    by_==

  @inline implicit def bigInteger: Reusability[jm.BigInteger] =
    byRefOr_==

  @inline implicit def bigInt: Reusability[sm.BigInt] =
    byRefOr_==

  // java.time._

  implicit def clock: Reusability[Clock] =
    Reusability.byRefOr_==

  implicit def dayOfWeek: Reusability[DayOfWeek] =
    Reusability.by_==

  implicit def localDate: Reusability[LocalDate] =
    Reusability.byRefOr_==

  implicit def month: Reusability[Month] =
    Reusability.by_==

  implicit def monthDay: Reusability[MonthDay] =
    Reusability.byRefOr_==

  implicit def period: Reusability[Period] =
    Reusability.byRefOr_==

  implicit def year: Reusability[Year] =
    Reusability.by_==

  implicit def yearMonth: Reusability[YearMonth] =
    Reusability.byRefOr_==

  implicit def zoneId: Reusability[ZoneId] =
    Reusability.by_==

  implicit def zoneOffset: Reusability[ZoneOffset] =
    Reusability.by_==

  // Refs

  /** Updating a reference doesn't trigger a component re-rendering, nor is the current reference value considered for reusability. */
  @inline implicit def refRaw[A]: Reusability[facade.React.RefHandle[A]] =
    byRef

  /** Updating a reference doesn't trigger a component re-rendering, nor is the current reference value considered for reusability. */
  implicit def refHandleF[F[_], A]: Reusability[Ref.HandleF[F, A]] =
    refRaw[A | Null].contramap(_.raw)

  /** Updating a reference doesn't trigger a component re-rendering, nor is the current reference value considered for reusability.
   *
   * Any `map`/`contramap` functions installed in the ref are ignored for the sake of reusability. If this is undesirable, pass around
   * a [[Reusable]] ref instead.
   */
  implicit def refFullF[F[_], I, A, O]: Reusability[Ref.FullF[F, I, A, O]] =
    refRaw[A | Null].contramap(_.raw)

  /** Updating a reference doesn't trigger a component re-rendering, nor is the current reference value considered for reusability.
   *
   * Any `map`/`contramap` functions installed in the ref are ignored for the sake of reusability. If this is undesirable, pass around
   * a [[Reusable]] ref instead.
   */
  implicit def refToComponentF[F[_], I, R, O, C]: Reusability[Ref.ToComponentF[F, I, R, O, C]] =
    refFullF[F, I, R, O].narrow

  /** Updating a reference doesn't trigger a component re-rendering, nor is the current reference value considered for reusability. */
  implicit def nonEmptyRefHandleF[F[_], A]: Reusability[NonEmptyRef.HandleF[F, A]] =
    refRaw[A].contramap(_.raw)

  /** Updating a reference doesn't trigger a component re-rendering, nor is the current reference value considered for reusability.
   *
   * Any `map`/`contramap` functions installed in the ref are ignored for the sake of reusability. If this is undesirable, pass around
   * a [[Reusable]] ref instead.
   */
  implicit def nonEmptyRefFullF[F[_], I, A, O]: Reusability[NonEmptyRef.FullF[F, I, A, O]] =
    refRaw[A].contramap(_.raw)

  /** Updating a reference doesn't trigger a component re-rendering, nor is the current reference value considered for reusability. */
  implicit def hooksUseRefF[F[_], A]: Reusability[Hooks.UseRefF[F, A]] =
    refRaw[A].contramap(_.raw)

  // Generated by bin/gen-reusable

  implicit def tuple2[A:Reusability, B:Reusability]: Reusability[(A,B)] =
    apply((x,y) => (x._1 ~=~ y._1) && (x._2 ~=~ y._2))

  implicit def tuple3[A:Reusability, B:Reusability, C:Reusability]: Reusability[(A,B,C)] =
    apply((x,y) => (x._1 ~=~ y._1) && (x._2 ~=~ y._2) && (x._3 ~=~ y._3))

  implicit def tuple4[A:Reusability, B:Reusability, C:Reusability, D:Reusability]: Reusability[(A,B,C,D)] =
    apply((x,y) => (x._1 ~=~ y._1) && (x._2 ~=~ y._2) && (x._3 ~=~ y._3) && (x._4 ~=~ y._4))

  implicit def tuple5[A:Reusability, B:Reusability, C:Reusability, D:Reusability, E:Reusability]: Reusability[(A,B,C,D,E)] =
    apply((x,y) => (x._1 ~=~ y._1) && (x._2 ~=~ y._2) && (x._3 ~=~ y._3) && (x._4 ~=~ y._4) && (x._5 ~=~ y._5))

  implicit def tuple6[A:Reusability, B:Reusability, C:Reusability, D:Reusability, E:Reusability, F:Reusability]: Reusability[(A,B,C,D,E,F)] =
    apply((x,y) => (x._1 ~=~ y._1) && (x._2 ~=~ y._2) && (x._3 ~=~ y._3) && (x._4 ~=~ y._4) && (x._5 ~=~ y._5) && (x._6 ~=~ y._6))

  implicit def tuple7[A:Reusability, B:Reusability, C:Reusability, D:Reusability, E:Reusability, F:Reusability, G:Reusability]: Reusability[(A,B,C,D,E,F,G)] =
    apply((x,y) => (x._1 ~=~ y._1) && (x._2 ~=~ y._2) && (x._3 ~=~ y._3) && (x._4 ~=~ y._4) && (x._5 ~=~ y._5) && (x._6 ~=~ y._6) && (x._7 ~=~ y._7))

  implicit def tuple8[A:Reusability, B:Reusability, C:Reusability, D:Reusability, E:Reusability, F:Reusability, G:Reusability, H:Reusability]: Reusability[(A,B,C,D,E,F,G,H)] =
    apply((x,y) => (x._1 ~=~ y._1) && (x._2 ~=~ y._2) && (x._3 ~=~ y._3) && (x._4 ~=~ y._4) && (x._5 ~=~ y._5) && (x._6 ~=~ y._6) && (x._7 ~=~ y._7) && (x._8 ~=~ y._8))

  implicit def tuple9[A:Reusability, B:Reusability, C:Reusability, D:Reusability, E:Reusability, F:Reusability, G:Reusability, H:Reusability, I:Reusability]: Reusability[(A,B,C,D,E,F,G,H,I)] =
    apply((x,y) => (x._1 ~=~ y._1) && (x._2 ~=~ y._2) && (x._3 ~=~ y._3) && (x._4 ~=~ y._4) && (x._5 ~=~ y._5) && (x._6 ~=~ y._6) && (x._7 ~=~ y._7) && (x._8 ~=~ y._8) && (x._9 ~=~ y._9))

  implicit def tuple10[A:Reusability, B:Reusability, C:Reusability, D:Reusability, E:Reusability, F:Reusability, G:Reusability, H:Reusability, I:Reusability, J:Reusability]: Reusability[(A,B,C,D,E,F,G,H,I,J)] =
    apply((x,y) => (x._1 ~=~ y._1) && (x._2 ~=~ y._2) && (x._3 ~=~ y._3) && (x._4 ~=~ y._4) && (x._5 ~=~ y._5) && (x._6 ~=~ y._6) && (x._7 ~=~ y._7) && (x._8 ~=~ y._8) && (x._9 ~=~ y._9) && (x._10 ~=~ y._10))

  implicit def tuple11[A:Reusability, B:Reusability, C:Reusability, D:Reusability, E:Reusability, F:Reusability, G:Reusability, H:Reusability, I:Reusability, J:Reusability, K:Reusability]: Reusability[(A,B,C,D,E,F,G,H,I,J,K)] =
    apply((x,y) => (x._1 ~=~ y._1) && (x._2 ~=~ y._2) && (x._3 ~=~ y._3) && (x._4 ~=~ y._4) && (x._5 ~=~ y._5) && (x._6 ~=~ y._6) && (x._7 ~=~ y._7) && (x._8 ~=~ y._8) && (x._9 ~=~ y._9) && (x._10 ~=~ y._10) && (x._11 ~=~ y._11))

  implicit def tuple12[A:Reusability, B:Reusability, C:Reusability, D:Reusability, E:Reusability, F:Reusability, G:Reusability, H:Reusability, I:Reusability, J:Reusability, K:Reusability, L:Reusability]: Reusability[(A,B,C,D,E,F,G,H,I,J,K,L)] =
    apply((x,y) => (x._1 ~=~ y._1) && (x._2 ~=~ y._2) && (x._3 ~=~ y._3) && (x._4 ~=~ y._4) && (x._5 ~=~ y._5) && (x._6 ~=~ y._6) && (x._7 ~=~ y._7) && (x._8 ~=~ y._8) && (x._9 ~=~ y._9) && (x._10 ~=~ y._10) && (x._11 ~=~ y._11) && (x._12 ~=~ y._12))

  implicit def tuple13[A:Reusability, B:Reusability, C:Reusability, D:Reusability, E:Reusability, F:Reusability, G:Reusability, H:Reusability, I:Reusability, J:Reusability, K:Reusability, L:Reusability, M:Reusability]: Reusability[(A,B,C,D,E,F,G,H,I,J,K,L,M)] =
    apply((x,y) => (x._1 ~=~ y._1) && (x._2 ~=~ y._2) && (x._3 ~=~ y._3) && (x._4 ~=~ y._4) && (x._5 ~=~ y._5) && (x._6 ~=~ y._6) && (x._7 ~=~ y._7) && (x._8 ~=~ y._8) && (x._9 ~=~ y._9) && (x._10 ~=~ y._10) && (x._11 ~=~ y._11) && (x._12 ~=~ y._12) && (x._13 ~=~ y._13))

  implicit def tuple14[A:Reusability, B:Reusability, C:Reusability, D:Reusability, E:Reusability, F:Reusability, G:Reusability, H:Reusability, I:Reusability, J:Reusability, K:Reusability, L:Reusability, M:Reusability, N:Reusability]: Reusability[(A,B,C,D,E,F,G,H,I,J,K,L,M,N)] =
    apply((x,y) => (x._1 ~=~ y._1) && (x._2 ~=~ y._2) && (x._3 ~=~ y._3) && (x._4 ~=~ y._4) && (x._5 ~=~ y._5) && (x._6 ~=~ y._6) && (x._7 ~=~ y._7) && (x._8 ~=~ y._8) && (x._9 ~=~ y._9) && (x._10 ~=~ y._10) && (x._11 ~=~ y._11) && (x._12 ~=~ y._12) && (x._13 ~=~ y._13) && (x._14 ~=~ y._14))

  implicit def tuple15[A:Reusability, B:Reusability, C:Reusability, D:Reusability, E:Reusability, F:Reusability, G:Reusability, H:Reusability, I:Reusability, J:Reusability, K:Reusability, L:Reusability, M:Reusability, N:Reusability, O:Reusability]: Reusability[(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O)] =
    apply((x,y) => (x._1 ~=~ y._1) && (x._2 ~=~ y._2) && (x._3 ~=~ y._3) && (x._4 ~=~ y._4) && (x._5 ~=~ y._5) && (x._6 ~=~ y._6) && (x._7 ~=~ y._7) && (x._8 ~=~ y._8) && (x._9 ~=~ y._9) && (x._10 ~=~ y._10) && (x._11 ~=~ y._11) && (x._12 ~=~ y._12) && (x._13 ~=~ y._13) && (x._14 ~=~ y._14) && (x._15 ~=~ y._15))

  implicit def tuple16[A:Reusability, B:Reusability, C:Reusability, D:Reusability, E:Reusability, F:Reusability, G:Reusability, H:Reusability, I:Reusability, J:Reusability, K:Reusability, L:Reusability, M:Reusability, N:Reusability, O:Reusability, P:Reusability]: Reusability[(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P)] =
    apply((x,y) => (x._1 ~=~ y._1) && (x._2 ~=~ y._2) && (x._3 ~=~ y._3) && (x._4 ~=~ y._4) && (x._5 ~=~ y._5) && (x._6 ~=~ y._6) && (x._7 ~=~ y._7) && (x._8 ~=~ y._8) && (x._9 ~=~ y._9) && (x._10 ~=~ y._10) && (x._11 ~=~ y._11) && (x._12 ~=~ y._12) && (x._13 ~=~ y._13) && (x._14 ~=~ y._14) && (x._15 ~=~ y._15) && (x._16 ~=~ y._16))

  implicit def tuple17[A:Reusability, B:Reusability, C:Reusability, D:Reusability, E:Reusability, F:Reusability, G:Reusability, H:Reusability, I:Reusability, J:Reusability, K:Reusability, L:Reusability, M:Reusability, N:Reusability, O:Reusability, P:Reusability, Q:Reusability]: Reusability[(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q)] =
    apply((x,y) => (x._1 ~=~ y._1) && (x._2 ~=~ y._2) && (x._3 ~=~ y._3) && (x._4 ~=~ y._4) && (x._5 ~=~ y._5) && (x._6 ~=~ y._6) && (x._7 ~=~ y._7) && (x._8 ~=~ y._8) && (x._9 ~=~ y._9) && (x._10 ~=~ y._10) && (x._11 ~=~ y._11) && (x._12 ~=~ y._12) && (x._13 ~=~ y._13) && (x._14 ~=~ y._14) && (x._15 ~=~ y._15) && (x._16 ~=~ y._16) && (x._17 ~=~ y._17))

  implicit def tuple18[A:Reusability, B:Reusability, C:Reusability, D:Reusability, E:Reusability, F:Reusability, G:Reusability, H:Reusability, I:Reusability, J:Reusability, K:Reusability, L:Reusability, M:Reusability, N:Reusability, O:Reusability, P:Reusability, Q:Reusability, R:Reusability]: Reusability[(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R)] =
    apply((x,y) => (x._1 ~=~ y._1) && (x._2 ~=~ y._2) && (x._3 ~=~ y._3) && (x._4 ~=~ y._4) && (x._5 ~=~ y._5) && (x._6 ~=~ y._6) && (x._7 ~=~ y._7) && (x._8 ~=~ y._8) && (x._9 ~=~ y._9) && (x._10 ~=~ y._10) && (x._11 ~=~ y._11) && (x._12 ~=~ y._12) && (x._13 ~=~ y._13) && (x._14 ~=~ y._14) && (x._15 ~=~ y._15) && (x._16 ~=~ y._16) && (x._17 ~=~ y._17) && (x._18 ~=~ y._18))

  implicit def tuple19[A:Reusability, B:Reusability, C:Reusability, D:Reusability, E:Reusability, F:Reusability, G:Reusability, H:Reusability, I:Reusability, J:Reusability, K:Reusability, L:Reusability, M:Reusability, N:Reusability, O:Reusability, P:Reusability, Q:Reusability, R:Reusability, S:Reusability]: Reusability[(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S)] =
    apply((x,y) => (x._1 ~=~ y._1) && (x._2 ~=~ y._2) && (x._3 ~=~ y._3) && (x._4 ~=~ y._4) && (x._5 ~=~ y._5) && (x._6 ~=~ y._6) && (x._7 ~=~ y._7) && (x._8 ~=~ y._8) && (x._9 ~=~ y._9) && (x._10 ~=~ y._10) && (x._11 ~=~ y._11) && (x._12 ~=~ y._12) && (x._13 ~=~ y._13) && (x._14 ~=~ y._14) && (x._15 ~=~ y._15) && (x._16 ~=~ y._16) && (x._17 ~=~ y._17) && (x._18 ~=~ y._18) && (x._19 ~=~ y._19))

  implicit def tuple20[A:Reusability, B:Reusability, C:Reusability, D:Reusability, E:Reusability, F:Reusability, G:Reusability, H:Reusability, I:Reusability, J:Reusability, K:Reusability, L:Reusability, M:Reusability, N:Reusability, O:Reusability, P:Reusability, Q:Reusability, R:Reusability, S:Reusability, T:Reusability]: Reusability[(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T)] =
    apply((x,y) => (x._1 ~=~ y._1) && (x._2 ~=~ y._2) && (x._3 ~=~ y._3) && (x._4 ~=~ y._4) && (x._5 ~=~ y._5) && (x._6 ~=~ y._6) && (x._7 ~=~ y._7) && (x._8 ~=~ y._8) && (x._9 ~=~ y._9) && (x._10 ~=~ y._10) && (x._11 ~=~ y._11) && (x._12 ~=~ y._12) && (x._13 ~=~ y._13) && (x._14 ~=~ y._14) && (x._15 ~=~ y._15) && (x._16 ~=~ y._16) && (x._17 ~=~ y._17) && (x._18 ~=~ y._18) && (x._19 ~=~ y._19) && (x._20 ~=~ y._20))

  implicit def tuple21[A:Reusability, B:Reusability, C:Reusability, D:Reusability, E:Reusability, F:Reusability, G:Reusability, H:Reusability, I:Reusability, J:Reusability, K:Reusability, L:Reusability, M:Reusability, N:Reusability, O:Reusability, P:Reusability, Q:Reusability, R:Reusability, S:Reusability, T:Reusability, U:Reusability]: Reusability[(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U)] =
    apply((x,y) => (x._1 ~=~ y._1) && (x._2 ~=~ y._2) && (x._3 ~=~ y._3) && (x._4 ~=~ y._4) && (x._5 ~=~ y._5) && (x._6 ~=~ y._6) && (x._7 ~=~ y._7) && (x._8 ~=~ y._8) && (x._9 ~=~ y._9) && (x._10 ~=~ y._10) && (x._11 ~=~ y._11) && (x._12 ~=~ y._12) && (x._13 ~=~ y._13) && (x._14 ~=~ y._14) && (x._15 ~=~ y._15) && (x._16 ~=~ y._16) && (x._17 ~=~ y._17) && (x._18 ~=~ y._18) && (x._19 ~=~ y._19) && (x._20 ~=~ y._20) && (x._21 ~=~ y._21))

  implicit def tuple22[A:Reusability, B:Reusability, C:Reusability, D:Reusability, E:Reusability, F:Reusability, G:Reusability, H:Reusability, I:Reusability, J:Reusability, K:Reusability, L:Reusability, M:Reusability, N:Reusability, O:Reusability, P:Reusability, Q:Reusability, R:Reusability, S:Reusability, T:Reusability, U:Reusability, V:Reusability]: Reusability[(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V)] =
    apply((x,y) => (x._1 ~=~ y._1) && (x._2 ~=~ y._2) && (x._3 ~=~ y._3) && (x._4 ~=~ y._4) && (x._5 ~=~ y._5) && (x._6 ~=~ y._6) && (x._7 ~=~ y._7) && (x._8 ~=~ y._8) && (x._9 ~=~ y._9) && (x._10 ~=~ y._10) && (x._11 ~=~ y._11) && (x._12 ~=~ y._12) && (x._13 ~=~ y._13) && (x._14 ~=~ y._14) && (x._15 ~=~ y._15) && (x._16 ~=~ y._16) && (x._17 ~=~ y._17) && (x._18 ~=~ y._18) && (x._19 ~=~ y._19) && (x._20 ~=~ y._20) && (x._21 ~=~ y._21) && (x._22 ~=~ y._22))

  // ===================================================================================================================

  object MapImplicits {
    implicit def reusabilityMap[K, V](implicit rv: Reusability[V]): Reusability[Map[K, V]] =
      Reusability.byRef || Reusability.map
  }

  object DecimalImplicitsWithoutTolerance {
    @inline implicit def reusabilityJavaBigDecimal : Reusability[jm.BigDecimal] = byRefOr_==
    @inline implicit def reusabilityScalaBigDecimal: Reusability[sm.BigDecimal] = byRefOr_==
    @inline implicit def reusabilityDouble         : Reusability[Double       ] = by_==
    @inline implicit def reusabilityFloat          : Reusability[Float        ] = by_==
  }

  object TemporalImplicitsWithoutTolerance {

    implicit lazy val reusabilityJavaDuration: Reusability[Duration] =
      byRefOr_==

    implicit lazy val reusabilityInstant: Reusability[Instant] =
      byRefOr_==

    implicit lazy val reusabilityLocalDateTime: Reusability[LocalDateTime] =
      byRefOr_==

    implicit lazy val reusabilityLocalDate: Reusability[LocalDate] =
      byRefOr_==

    implicit lazy val reusabilityOffsetDateTime: Reusability[OffsetDateTime] =
      byRefOr_==

    implicit lazy val reusabilityOffsetTime: Reusability[OffsetTime] =
      byRefOr_==

    implicit lazy val reusabilityZonedDateTime: Reusability[ZonedDateTime] =
      byRefOr_==

    implicit lazy val reusabilityScalaDuration: Reusability[scd.Duration] =
      byRefOr_==

    implicit lazy val reusabilityFiniteDuration: Reusability[scd.FiniteDuration] =
      byRefOr_==

    implicit lazy val reusabilityDeadline: Reusability[scd.Deadline] =
      byRefOr_==
  }

  // ===================================================================================================================

  def shouldComponentUpdateAnd[G[_], P: Reusability, C <: Children, S: Reusability, B, U <: UpdateSnapshot](f: ShouldComponentUpdateResult[P, S, B] => G[Unit])(implicit G: Sync[G]): ScalaComponent.Config[P, C, S, B, U, U] =
    _.shouldComponentUpdate { i =>
      val r = ShouldComponentUpdateResult(i)
      val g = f(r)
      G.map(g)(_ => r.update)
    }

  def shouldComponentUpdateAndLog[P: Reusability, C <: Children, S: Reusability, B, U <: UpdateSnapshot](name: String): ScalaComponent.Config[P, C, S, B, U, U] = {
    import DefaultEffects.Sync
    shouldComponentUpdateAnd(_.log(name))
  }

  final case class ShouldComponentUpdateResult[P: Reusability, S: Reusability, B](self: ScalaComponent.Lifecycle.ShouldComponentUpdate[P, S, B]) {
    def mounted       = self.mountedImpure
    def backend       = self.backend
    def propsChildren = self.propsChildren
    def currentProps  = self.currentProps
    def currentState  = self.currentState
    def nextProps     = self.nextProps
    def nextState     = self.nextState
    def getDOMNode    = self.getDOMNode

    val updateProps: Boolean = currentProps ~/~ nextProps
    val updateState: Boolean = currentState ~/~ nextState
    val update     : Boolean = updateProps || updateState

    def log[F[_]](name: String)(implicit F: Sync[F]): F[Unit] =
      F.delay(
        console.log(
          s"""
             |s"$name.shouldComponentUpdate = $update
             |  Props: $updateProps. [$currentProps] ⇒ [$nextProps]
             |  State: $updateState. [$currentState] ⇒ [$nextState]
           """.stripMargin
        )
      )
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy