diff --git a/library/coreGeneric/src/main/scala/japgolly/scalajs/react/hooks/Api.scala b/library/coreGeneric/src/main/scala/japgolly/scalajs/react/hooks/Api.scala index e4fb0d0cc..39fee8369 100644 --- a/library/coreGeneric/src/main/scala/japgolly/scalajs/react/hooks/Api.scala +++ b/library/coreGeneric/src/main/scala/japgolly/scalajs/react/hooks/Api.scala @@ -6,6 +6,7 @@ import japgolly.scalajs.react.feature.Context import japgolly.scalajs.react.hooks.Hooks._ import japgolly.scalajs.react.internal.Box import japgolly.scalajs.react.util.DefaultEffects +import japgolly.scalajs.react.util.Effect.Sync import japgolly.scalajs.react.util.Util.identityFn import japgolly.scalajs.react.vdom.{TopNode, VdomNode} import japgolly.scalajs.react.{Children, CtorType, Ref, Reusability, Reusable} @@ -195,8 +196,7 @@ object Api { * * By default, effects run after every completed render. * If you'd only like to execute the effect when your component is mounted, then use [[useEffectOnMount]]. - * If you'd only like to execute the effect when certain values have changed, provide those certain values as - * the second argument. + * If you'd only like to execute the effect when certain values have changed, then use [[useEffectWithDeps]]. * * @see https://reactjs.org/docs/hooks-reference.html#useeffect */ @@ -208,13 +208,12 @@ object Api { * * By default, effects run after every completed render. * If you'd only like to execute the effect when your component is mounted, then use [[useEffectOnMount]]. - * If you'd only like to execute the effect when certain values have changed, provide those certain values as - * the second argument. + * If you'd only like to execute the effect when certain values have changed, then use [[useEffectWithDeps]]. * * @see https://reactjs.org/docs/hooks-reference.html#useeffect */ - final def useEffectBy[A](init: Ctx => A)(implicit a: UseEffectArg[A], step: Step): step.Self = - self(ctx => UseEffect.unsafeCreate(init(ctx))) + final def useEffectBy[A](effect: Ctx => A)(implicit a: UseEffectArg[A], step: Step): step.Self = + self(ctx => UseEffect.unsafeCreate(effect(ctx))) /** The callback passed to useEffect will run after the render is committed to the screen. Think of effects as an * escape hatch from React’s purely functional world into the imperative world. @@ -239,7 +238,7 @@ object Api { /** The callback passed to useEffect will run after the render is committed to the screen. Think of effects as an * escape hatch from React’s purely functional world into the imperative world. * - * This will only execute the effect when values in the second argument, change. + * This will only execute the effect when values in the first argument change. * * @see https://reactjs.org/docs/hooks-reference.html#useeffect */ @@ -249,7 +248,7 @@ object Api { /** The callback passed to useEffect will run after the render is committed to the screen. Think of effects as an * escape hatch from React’s purely functional world into the imperative world. * - * This will only execute the effect when values in the second argument, change. + * This will only execute the effect when values in the first argument change. * * @see https://reactjs.org/docs/hooks-reference.html#useeffect */ @@ -267,8 +266,7 @@ object Api { * Prefer the standard [[useEffect]] when possible to avoid blocking visual updates. * * If you'd only like to execute the effect when your component is mounted, then use [[useLayoutEffectOnMount]]. - * If you'd only like to execute the effect when certain values have changed, provide those certain values as - * the second argument. + * If you'd only like to execute the effect when certain values have changed, then use [[useLayoutEffectWithDeps]]. * * @see https://reactjs.org/docs/hooks-reference.html#useLayoutEffect */ @@ -282,13 +280,12 @@ object Api { * Prefer the standard [[useEffect]] when possible to avoid blocking visual updates. * * If you'd only like to execute the effect when your component is mounted, then use [[useLayoutEffectOnMount]]. - * If you'd only like to execute the effect when certain values have changed, provide those certain values as - * the second argument. + * If you'd only like to execute the effect when certain values have changed, then use [[useLayoutEffectWithDeps]]. * * @see https://reactjs.org/docs/hooks-reference.html#useLayoutEffect */ - final def useLayoutEffectBy[A](init: Ctx => A)(implicit a: UseEffectArg[A], step: Step): step.Self = - self(ctx => UseEffect.unsafeCreateLayout(init(ctx))) + final def useLayoutEffectBy[A](effect: Ctx => A)(implicit a: UseEffectArg[A], step: Step): step.Self = + self(ctx => UseEffect.unsafeCreateLayout(effect(ctx))) /** The signature is identical to [[useEffect]], but it fires synchronously after all DOM mutations. Use this to * read layout from the DOM and synchronously re-render. Updates scheduled inside useLayoutEffect will be flushed @@ -322,7 +319,7 @@ object Api { * * Prefer the standard [[useEffect]] when possible to avoid blocking visual updates. * - * This will only execute the effect when values in the second argument, change. + * This will only execute the effect when values in the first argument change. * * @see https://reactjs.org/docs/hooks-reference.html#useLayoutEffect */ @@ -335,13 +332,93 @@ object Api { * * Prefer the standard [[useEffect]] when possible to avoid blocking visual updates. * - * This will only execute the effect when values in the second argument, change. + * This will only execute the effect when values in the first argument change. * * @see https://reactjs.org/docs/hooks-reference.html#useLayoutEffect */ final def useLayoutEffectWithDepsBy[D, A](deps: Ctx => D)(effect: Ctx => D => A)(implicit a: UseEffectArg[A], r: Reusability[D], step: Step): step.Self = customBy(ctx => ReusableEffect.useLayoutEffect(deps(ctx))(effect(ctx))) + /** The signature is identical to [[useEffect]], but it fires synchronously after all DOM mutations, but before any + * layout Effects fire. Use this to insert styles before any Effects fire that may need to read layout. Updates + * scheduled inside useLayoutEffect will be flushed synchronously, before the browser has a chance to paint. + * + * Prefer the standard [[useEffect]] when possible to avoid blocking visual updates. + * + * If you'd only like to execute the effect when your component is mounted, then use [[useInsertionEffectOnMount]]. + * If you'd only like to execute the effect when certain values have changed, then use [[useInsertionEffectWithDeps]]. + * + * @see https://react.dev/reference/react/useInsertionEffect#useInsertionEffect + */ + final def useInsertionEffect[A](effect: A)(implicit a: UseEffectArg[A], step: Step): step.Self = + useInsertionEffectBy(_ => effect) + + /** The signature is identical to [[useEffect]], but it fires synchronously after all DOM mutations, but before any + * layout Effects fire. Use this to insert styles before any Effects fire that may need to read layout. Updates + * scheduled inside useLayoutEffect will be flushed synchronously, before the browser has a chance to paint. + * + * Prefer the standard [[useEffect]] when possible to avoid blocking visual updates. + * + * If you'd only like to execute the effect when your component is mounted, then use [[useInsertionEffectOnMount]]. + * If you'd only like to execute the effect when certain values have changed, then use [[useInsertionEffectWithDeps]]. + * + * @see https://react.dev/reference/react/useInsertionEffect#useInsertionEffect + */ + final def useInsertionEffectBy[A](effect: Ctx => A)(implicit a: UseEffectArg[A], step: Step): step.Self = + self(ctx => UseEffect.unsafeCreateInsertion(effect(ctx))) + + /** The signature is identical to [[useEffect]], but it fires synchronously after all DOM mutations, but before any + * layout Effects fire. Use this to insert styles before any Effects fire that may need to read layout. Updates + * scheduled inside useLayoutEffect will be flushed synchronously, before the browser has a chance to paint. + * + * Prefer the standard [[useEffect]] when possible to avoid blocking visual updates. + * + * This will only execute the effect when your component is mounted. + * + * @see https://react.dev/reference/react/useInsertionEffect#useInsertionEffect + */ + final def useInsertionEffectOnMount[A](effect: A)(implicit a: UseEffectArg[A], step: Step): step.Self = + useInsertionEffectOnMountBy(_ => effect) + + /** The signature is identical to [[useEffect]], but it fires synchronously after all DOM mutations, but before any + * layout Effects fire. Use this to insert styles before any Effects fire that may need to read layout. Updates + * scheduled inside useLayoutEffect will be flushed synchronously, before the browser has a chance to paint. + * + * Prefer the standard [[useEffect]] when possible to avoid blocking visual updates. + * + * This will only execute the effect when your component is mounted. + * + * @see https://react.dev/reference/react/useInsertionEffect#useInsertionEffect + */ + final def useInsertionEffectOnMountBy[A](effect: Ctx => A)(implicit a: UseEffectArg[A], step: Step): step.Self = + self(ctx => UseEffect.unsafeCreateInsertionOnMount(effect(ctx))) + + /** The signature is identical to [[useEffect]], but it fires synchronously after all DOM mutations, but before any + * layout Effects fire. Use this to insert styles before any Effects fire that may need to read layout. Updates + * scheduled inside useLayoutEffect will be flushed synchronously, before the browser has a chance to paint. + * + * Prefer the standard [[useEffect]] when possible to avoid blocking visual updates. + * + * This will only execute the effect when values in the first argument change. + * + * @see https://react.dev/reference/react/useInsertionEffect#useInsertionEffect + */ + final def useInsertionEffectWithDeps[D, A](deps: => D)(effect: D => A)(implicit a: UseEffectArg[A], r: Reusability[D], step: Step): step.Self = + custom(ReusableEffect.useInsertionEffect(deps)(effect)) + + /** The signature is identical to [[useEffect]], but it fires synchronously after all DOM mutations, but before any + * layout Effects fire. Use this to insert styles before any Effects fire that may need to read layout. Updates + * scheduled inside useLayoutEffect will be flushed synchronously, before the browser has a chance to paint. + * + * Prefer the standard [[useEffect]] when possible to avoid blocking visual updates. + * + * This will only execute the effect when values in the first argument change. + * + * @see https://react.dev/reference/react/useInsertionEffect#useInsertionEffect + */ + final def useInsertionEffectWithDepsBy[D, A](deps: Ctx => D)(effect: Ctx => D => A)(implicit a: UseEffectArg[A], r: Reusability[D], step: Step): step.Self = + customBy(ctx => ReusableEffect.useInsertionEffect(deps(ctx))(effect(ctx))) + /** Returns a memoized value. * * Pass a “create” function and any dependencies. useMemo will only recompute the memoized value when one @@ -478,7 +555,7 @@ object Api { final def useStateWithReuseBy[S: ClassTag: Reusability](initialState: Ctx => S)(implicit step: Step): step.Next[UseStateWithReuse[S]] = next(ctx => UseStateWithReuse.unsafeCreate(initialState(ctx))) - /** `useId` is a React Hook for generating unique IDs that can be passed to accessibility attributes. + /** Generates unique IDs that can be passed to accessibility attributes. * * @see https://react.dev/reference/react/useId */ @@ -496,6 +573,62 @@ object Api { */ final def useTransition(implicit step: Step): step.Next[UseTransition] = customBy(_ => UseTransition()) + + /** + * Lets you subscribe to an external store. + * + * @see + * {@link https://react.dev/reference/react/useSyncExternalStore} + */ + @inline final def useSyncExternalStore[F[_], A](subscribe: F[Unit] => F[F[Unit]], getSnapshot: F[A])(implicit F: Sync[F], step: Step): step.Next[A] = + useSyncExternalStore(subscribe, getSnapshot, js.undefined) + + /** + * Lets you subscribe to an external store. + * + * @see + * {@link https://react.dev/reference/react/useSyncExternalStore} + */ + final def useSyncExternalStore[F[_], A](subscribe: F[Unit] => F[F[Unit]], getSnapshot: F[A], getServerSnapshot: js.UndefOr[F[A]])(implicit F: Sync[F], step: Step): step.Next[A] = + customBy(_ => UseSyncExternalStore(subscribe, getSnapshot, getServerSnapshot)) + + /** + * Lets you subscribe to an external store. + * + * @see + * {@link https://react.dev/reference/react/useSyncExternalStore} + */ + @inline final def useSyncExternalStoreBy[F[_], A](subscribe: Ctx => F[Unit] => F[F[Unit]], getSnapshot: Ctx => F[A])(implicit F: Sync[F], step: Step): step.Next[A] = + useSyncExternalStoreBy(subscribe, getSnapshot, js.undefined) + /** + * Lets you subscribe to an external store. + * + * @see + * {@link https://react.dev/reference/react/useSyncExternalStore} + */ + final def useSyncExternalStoreBy[F[_], A](subscribe: Ctx => F[Unit] => F[F[Unit]], getSnapshot: Ctx => F[A], getServerSnapshot: js.UndefOr[Ctx => F[A]])(implicit F: Sync[F], step: Step): step.Next[A] = + customBy(ctx => UseSyncExternalStore(subscribe(ctx), getSnapshot(ctx), getServerSnapshot.map(_(ctx)))) + + /** + * Lets you defer updating a part of the UI. + * + * @see + * {@link https://react.dev/reference/react/useDeferredValue} + */ + final def useDeferredValue[A](value: Ctx => A)(implicit step: Step): step.Next[A] = + // initialValue was added in React 19 - Replace when we upgrade to React 19 + // customBy(ctx => UseDeferredValue(value(ctx), js.undefined)) + customBy(ctx => UseDeferredValue(value(ctx))) + + // initialValue was added in React 19 - Uncomment when we upgrade to React 19 + // /** + // * Lets you defer updating a part of the UI. + // * + // * @see + // * {@link https://react.dev/reference/react/useDeferredValue} + // */ + // final def useDeferredValue[A](value: Ctx => A, initialValue: Ctx => A)(implicit step: Step): step.Next[A] = + // customBy(ctx => UseDeferredValue(value(ctx), initialValue(ctx))) } // =================================================================================================================== @@ -573,18 +706,17 @@ object Api { * * By default, effects run after every completed render. * If you'd only like to execute the effect when your component is mounted, then use [[useEffectOnMount]]. - * If you'd only like to execute the effect when certain values have changed, provide those certain values as - * the second argument. + * If you'd only like to execute the effect when certain values have changed, then use [[useEffectWithDeps]]. * * @see https://reactjs.org/docs/hooks-reference.html#useeffect */ - final def useEffectBy[A](init: CtxFn[A])(implicit a: UseEffectArg[A], step: Step): step.Self = - useEffectBy(step.squash(init)(_)) + final def useEffectBy[A](effect: CtxFn[A])(implicit a: UseEffectArg[A], step: Step): step.Self = + useEffectBy(step.squash(effect)(_)) /** The callback passed to useEffect will run after the render is committed to the screen. Think of effects as an * escape hatch from React’s purely functional world into the imperative world. * - * This will only execute the effect when values in the second argument, change. + * This will only execute the effect when values in the first argument change. * * @see https://reactjs.org/docs/hooks-reference.html#useeffect */ @@ -608,13 +740,12 @@ object Api { * Prefer the standard [[useEffect]] when possible to avoid blocking visual updates. * * If you'd only like to execute the effect when your component is mounted, then use [[useLayoutEffectOnMount]]. - * If you'd only like to execute the effect when certain values have changed, provide those certain values as - * the second argument. + * If you'd only like to execute the effect when certain values have changed, then use [[useLayoutEffectWithDeps]]. * * @see https://reactjs.org/docs/hooks-reference.html#useLayoutEffect */ - final def useLayoutEffectBy[A](init: CtxFn[A])(implicit a: UseEffectArg[A], step: Step): step.Self = - useLayoutEffectBy(step.squash(init)(_)) + final def useLayoutEffectBy[A](effect: CtxFn[A])(implicit a: UseEffectArg[A], step: Step): step.Self = + useLayoutEffectBy(step.squash(effect)(_)) /** The signature is identical to [[useEffect]], but it fires synchronously after all DOM mutations. Use this to * read layout from the DOM and synchronously re-render. Updates scheduled inside useLayoutEffect will be flushed @@ -622,7 +753,7 @@ object Api { * * Prefer the standard [[useEffect]] when possible to avoid blocking visual updates. * - * This will only execute the effect when values in the second argument, change. + * This will only execute the effect when values in the first argument change. * * @see https://reactjs.org/docs/hooks-reference.html#useLayoutEffect */ @@ -642,6 +773,46 @@ object Api { final def useLayoutEffectOnMountBy[A](effect: CtxFn[A])(implicit a: UseEffectArg[A], step: Step): step.Self = useLayoutEffectOnMountBy(step.squash(effect)(_)) + /** The signature is identical to [[useEffect]], but it fires synchronously after all DOM mutations, but before any + * layout Effects fire. Use this to insert styles before any Effects fire that may need to read layout. Updates + * scheduled inside useLayoutEffect will be flushed synchronously, before the browser has a chance to paint. + * + * Prefer the standard [[useEffect]] when possible to avoid blocking visual updates. + * + * If you'd only like to execute the effect when your component is mounted, then use [[useInsertionEffectOnMount]]. + * If you'd only like to execute the effect when certain values have changed, then use [[useInsertionEffectWithDeps]]. + * + * @see https://react.dev/reference/react/useInsertionEffect#useInsertionEffect + */ + final def useInsertionEffectBy[A](effect: CtxFn[A])(implicit a: UseEffectArg[A], step: Step): step.Self = + useInsertionEffectBy(step.squash(effect)(_)) + + /** The signature is identical to [[useEffect]], but it fires synchronously after all DOM mutations, but before any + * layout Effects fire. Use this to insert styles before any Effects fire that may need to read layout. Updates + * scheduled inside useLayoutEffect will be flushed synchronously, before the browser has a chance to paint. + * + * Prefer the standard [[useEffect]] when possible to avoid blocking visual updates. + * + * This will only execute the effect when values in the first argument change. + * + * @see https://react.dev/reference/react/useInsertionEffect#useInsertionEffect + */ + final def useInsertionEffectWithDepsBy[D, A](deps: CtxFn[D])(effect: CtxFn[D => A])(implicit a: UseEffectArg[A], r: Reusability[D], step: Step): step.Self = + useInsertionEffectWithDepsBy(step.squash(deps)(_))(step.squash(effect)(_)) + + /** The signature is identical to [[useEffect]], but it fires synchronously after all DOM mutations, but before any + * layout Effects fire. Use this to insert styles before any Effects fire that may need to read layout. Updates + * scheduled inside useLayoutEffect will be flushed synchronously, before the browser has a chance to paint. + * + * Prefer the standard [[useEffect]] when possible to avoid blocking visual updates. + * + * This will only execute the effect when your component is mounted. + * + * @see https://react.dev/reference/react/useInsertionEffect#useInsertionEffect + */ + final def useInsertionEffectOnMountBy[A](effect: CtxFn[A])(implicit a: UseEffectArg[A], step: Step): step.Self = + useInsertionEffectOnMountBy(step.squash(effect)(_)) + /** Returns a memoized value. * * Pass a “create” function and any dependencies. useMemo will only recompute the memoized value when one @@ -693,6 +864,43 @@ object Api { */ final def useStateWithReuseBy[S: ClassTag: Reusability](initialState: CtxFn[S])(implicit step: Step): step.Next[UseStateWithReuse[S]] = useStateWithReuseBy(step.squash(initialState)(_)) + + /** + * Lets you subscribe to an external store. + * + * @see + * {@link https://react.dev/reference/react/useSyncExternalStore} + */ + @inline final def useSyncExternalStoreBy[F[_], A](subscribe: CtxFn[F[Unit] => F[F[Unit]]], getSnapshot: CtxFn[F[A]])(implicit F: Sync[F], step: Step): step.Next[A] = + useSyncExternalStoreBy(subscribe, getSnapshot, js.undefined) + /** + * Lets you subscribe to an external store. + * + * @see + * {@link https://react.dev/reference/react/useSyncExternalStore} + */ + final def useSyncExternalStoreBy[F[_], A](subscribe: CtxFn[F[Unit] => F[F[Unit]]], getSnapshot: CtxFn[F[A]], getServerSnapshot: js.UndefOr[CtxFn[F[A]]])(implicit F: Sync[F], step: Step): step.Next[A] = + useSyncExternalStoreBy(ctx => step.squash(subscribe)(ctx), ctx => step.squash(getSnapshot)(ctx), getServerSnapshot.map(f => ctx => step.squash(f)(ctx))) + + /** + * Lets you defer updating a part of the UI. + * + * @see + * {@link https://react.dev/reference/react/useDeferredValue} + */ + @inline final def useDeferredValue[A](value: CtxFn[A])(implicit step: Step): step.Next[A] = + useDeferredValue(ctx => step.squash(value)(ctx)) + + // initialValue was added in React 19 - Uncomment when we upgrade to React 19 + // /** + // * Lets you defer updating a part of the UI. + // * + // * @see + // * {@link https://react.dev/reference/react/useDeferredValue} + // */ + // final def useDeferredValue[A](value: CtxFn[A], initialValue: CtxFn[A])(implicit step: Step): step.Next[A] = + // // useDeferredValue(ctx => step.squash(value)(ctx), initialValue.map(f => ctx => step.squash(f)(ctx))) + // useDeferredValue(ctx => step.squash(value)(ctx), ctx => step.squash(initialValue)(ctx)) } // =================================================================================================================== diff --git a/library/coreGeneric/src/main/scala/japgolly/scalajs/react/hooks/Hooks.scala b/library/coreGeneric/src/main/scala/japgolly/scalajs/react/hooks/Hooks.scala index 30b4ef6ca..d9a58e5a9 100644 --- a/library/coreGeneric/src/main/scala/japgolly/scalajs/react/hooks/Hooks.scala +++ b/library/coreGeneric/src/main/scala/japgolly/scalajs/react/hooks/Hooks.scala @@ -31,10 +31,15 @@ object Hooks { override def fromJs = g } - implicit def callback[F[_]](implicit F: Dispatch[F]): UseCallbackArg[F[Unit]] = + implicit def dispatchUnit[F[_]](implicit F: Dispatch[F]): UseCallbackArg[F[Unit]] = apply[F[Unit], js.Function0[Unit]]( F.dispatchFn(_))( f => Reusable.byRef(f).withValue(F.delay(f()))) + + implicit def syncValue[F[_], A](implicit F: UnsafeSync[F]): UseCallbackArg[F[A]] = + apply[F[A], js.Function0[A]]( + F.toJsFn(_))( + f => Reusable.byRef(f).withValue(F.delay(f()))) } object UseCallback { @@ -100,6 +105,12 @@ object Hooks { def unsafeCreateLayoutOnMount[A](effect: A)(implicit a: UseEffectArg[A]): Unit = facade.React.useLayoutEffect(a.toJs(effect), new js.Array[Any]) + + def unsafeCreateInsertion[A](effect: A)(implicit a: UseEffectArg[A]): Unit = + facade.React.useInsertionEffect(a.toJs(effect)) + + def unsafeCreateInsertionOnMount[A](effect: A)(implicit a: UseEffectArg[A]): Unit = + facade.React.useInsertionEffect(a.toJs(effect), new js.Array[Any]) } object ReusableEffect { @@ -113,6 +124,11 @@ object Hooks { CustomHook.reusableDeps[D] .apply(() => deps) .map { case (d, rev) => facade.React.useLayoutEffect(a.toJs(effect(d)), js.Array[Int](rev)) } + + def useInsertionEffect[D, A](deps: => D)(effect: D => A)(implicit a: UseEffectArg[A], r: Reusability[D]): CustomHook[Unit, Unit] = + CustomHook.reusableDeps[D] + .apply(() => deps) + .map { case (d, rev) => facade.React.useInsertionEffect(a.toJs(effect(d)), js.Array[Int](rev)) } } // =================================================================================================================== @@ -259,9 +275,9 @@ object Hooks { } object UseStateF { - def apply[F[_], S, O](r: facade.React.UseState[S], oss: Reusable[facade.React.UseStateSetter[O]])(implicit f: Sync[F]): UseStateF[F, S] = + def apply[F[_], S, O](r: facade.React.UseState[S], oss: Reusable[facade.React.UseStateSetter[O]])(implicit FF: Sync[F]): UseStateF[F, S] = new UseStateF[F, S] { - override protected[hooks] implicit def F: Sync[F] = f + override protected[hooks] implicit def F: Sync[F] = FF override val raw = r override type OriginalState = O override val originalSetState = oss @@ -456,14 +472,6 @@ object Hooks { CustomHook.delay(UseTransitionF(facade.React.useTransition())(D.Sync)) } - object UseTransitionF { - def apply[F[_]](r: facade.React.UseTransition)(implicit f: Sync[F]): UseTransitionF[F] = - new UseTransitionF[F] { - override protected[hooks] implicit def F: Sync[F] = f - override val raw: React.UseTransition = r - } - } - trait UseTransitionF[F[_]] { protected[hooks] implicit def F: Sync[F] val raw: facade.React.UseTransition @@ -477,4 +485,31 @@ object Hooks { def startTransition(cb: => F[Unit]): F[Unit] = F.delay(raw._2(F.toJsFn(cb))) } + + object UseTransitionF { + def apply[F[_]](r: facade.React.UseTransition)(implicit FF: Sync[F]): UseTransitionF[F] = + new UseTransitionF[F] { + override protected[hooks] implicit def F: Sync[F] = FF + + override val raw: React.UseTransition = r + } + } + + object UseSyncExternalStore { + def apply[F[_], A](subscribe: F[Unit] => F[F[Unit]], getSnapshot: F[A], getServerSnapshot: js.UndefOr[F[A]] = js.undefined)(implicit F: Sync[F]): CustomHook[Unit, A] = { + val subscribeJs: facade.React.UseSyncExternalStoreSubscribeArg = (update: js.Function0[Unit]) => F.runSync(F.map(subscribe(F.fromJsFn0(update)))(F.toJsFn(_))) + val getSnapshotJs: js.Function0[A] = F.toJsFn(getSnapshot) + val getServerSnapshotJs: js.UndefOr[js.Function0[A]] = getServerSnapshot.map(F.toJsFn(_)) + CustomHook.delay(facade.React.useSyncExternalStore(subscribeJs, getSnapshotJs, getServerSnapshotJs)) + } + } + + object UseDeferredValue { + // initialValue was added in React 19 - Replace when we upgrade to React 19 + // def apply[A](value: A, initialValue: js.UndefOr[A] = js.undefined): CustomHook[Unit, A] = + // CustomHook.delay(facade.React.useDeferredValue(value, initialValue)) + + def apply[A](value: A): CustomHook[Unit, A] = + CustomHook.delay(facade.React.useDeferredValue(value)) + } } diff --git a/library/coreGeneric/src/main/scala/japgolly/scalajs/react/hooks/UseCallbackBoilerplate.scala b/library/coreGeneric/src/main/scala/japgolly/scalajs/react/hooks/UseCallbackBoilerplate.scala index b46f2e056..f21f448bb 100644 --- a/library/coreGeneric/src/main/scala/japgolly/scalajs/react/hooks/UseCallbackBoilerplate.scala +++ b/library/coreGeneric/src/main/scala/japgolly/scalajs/react/hooks/UseCallbackBoilerplate.scala @@ -20,109 +20,219 @@ trait UseCallbackArgInstances { z => (a) => Z.dispatch(z(a)))( z => Reusable.byRef(z).withValue((a) => Z.delay(z(a)))) + implicit def ci1[A, Y, Z[_]](implicit Z: UnsafeSync[Z]): UseCallbackArg[(A) => Z[Y]] = + UseCallbackArg[(A) => Z[Y], js.Function1[A, Y]]( + z => (a) => Z.runSync(z(a)))( + z => Reusable.byRef(z).withValue((a) => Z.delay(z(a)))) + implicit def c2[A, B, Z[_]](implicit Z: Dispatch[Z]): UseCallbackArg[(A, B) => Z[Unit]] = UseCallbackArg[(A, B) => Z[Unit], js.Function2[A, B, Unit]]( z => (a, b) => Z.dispatch(z(a, b)))( z => Reusable.byRef(z).withValue((a, b) => Z.delay(z(a, b)))) + implicit def ci2[A, B, Y, Z[_]](implicit Z: UnsafeSync[Z]): UseCallbackArg[(A, B) => Z[Y]] = + UseCallbackArg[(A, B) => Z[Y], js.Function2[A, B, Y]]( + z => (a, b) => Z.runSync(z(a, b)))( + z => Reusable.byRef(z).withValue((a, b) => Z.delay(z(a, b)))) + implicit def c3[A, B, C, Z[_]](implicit Z: Dispatch[Z]): UseCallbackArg[(A, B, C) => Z[Unit]] = UseCallbackArg[(A, B, C) => Z[Unit], js.Function3[A, B, C, Unit]]( z => (a, b, c) => Z.dispatch(z(a, b, c)))( z => Reusable.byRef(z).withValue((a, b, c) => Z.delay(z(a, b, c)))) + implicit def ci3[A, B, C, Y, Z[_]](implicit Z: UnsafeSync[Z]): UseCallbackArg[(A, B, C) => Z[Y]] = + UseCallbackArg[(A, B, C) => Z[Y], js.Function3[A, B, C, Y]]( + z => (a, b, c) => Z.runSync(z(a, b, c)))( + z => Reusable.byRef(z).withValue((a, b, c) => Z.delay(z(a, b, c)))) + implicit def c4[A, B, C, D, Z[_]](implicit Z: Dispatch[Z]): UseCallbackArg[(A, B, C, D) => Z[Unit]] = UseCallbackArg[(A, B, C, D) => Z[Unit], js.Function4[A, B, C, D, Unit]]( z => (a, b, c, d) => Z.dispatch(z(a, b, c, d)))( z => Reusable.byRef(z).withValue((a, b, c, d) => Z.delay(z(a, b, c, d)))) + implicit def ci4[A, B, C, D, Y, Z[_]](implicit Z: UnsafeSync[Z]): UseCallbackArg[(A, B, C, D) => Z[Y]] = + UseCallbackArg[(A, B, C, D) => Z[Y], js.Function4[A, B, C, D, Y]]( + z => (a, b, c, d) => Z.runSync(z(a, b, c, d)))( + z => Reusable.byRef(z).withValue((a, b, c, d) => Z.delay(z(a, b, c, d)))) + implicit def c5[A, B, C, D, E, Z[_]](implicit Z: Dispatch[Z]): UseCallbackArg[(A, B, C, D, E) => Z[Unit]] = UseCallbackArg[(A, B, C, D, E) => Z[Unit], js.Function5[A, B, C, D, E, Unit]]( z => (a, b, c, d, e) => Z.dispatch(z(a, b, c, d, e)))( z => Reusable.byRef(z).withValue((a, b, c, d, e) => Z.delay(z(a, b, c, d, e)))) + implicit def ci5[A, B, C, D, E, Y, Z[_]](implicit Z: UnsafeSync[Z]): UseCallbackArg[(A, B, C, D, E) => Z[Y]] = + UseCallbackArg[(A, B, C, D, E) => Z[Y], js.Function5[A, B, C, D, E, Y]]( + z => (a, b, c, d, e) => Z.runSync(z(a, b, c, d, e)))( + z => Reusable.byRef(z).withValue((a, b, c, d, e) => Z.delay(z(a, b, c, d, e)))) + implicit def c6[A, B, C, D, E, F, Z[_]](implicit Z: Dispatch[Z]): UseCallbackArg[(A, B, C, D, E, F) => Z[Unit]] = UseCallbackArg[(A, B, C, D, E, F) => Z[Unit], js.Function6[A, B, C, D, E, F, Unit]]( z => (a, b, c, d, e, f) => Z.dispatch(z(a, b, c, d, e, f)))( z => Reusable.byRef(z).withValue((a, b, c, d, e, f) => Z.delay(z(a, b, c, d, e, f)))) + implicit def ci6[A, B, C, D, E, F, Y, Z[_]](implicit Z: UnsafeSync[Z]): UseCallbackArg[(A, B, C, D, E, F) => Z[Y]] = + UseCallbackArg[(A, B, C, D, E, F) => Z[Y], js.Function6[A, B, C, D, E, F, Y]]( + z => (a, b, c, d, e, f) => Z.runSync(z(a, b, c, d, e, f)))( + z => Reusable.byRef(z).withValue((a, b, c, d, e, f) => Z.delay(z(a, b, c, d, e, f)))) + implicit def c7[A, B, C, D, E, F, G, Z[_]](implicit Z: Dispatch[Z]): UseCallbackArg[(A, B, C, D, E, F, G) => Z[Unit]] = UseCallbackArg[(A, B, C, D, E, F, G) => Z[Unit], js.Function7[A, B, C, D, E, F, G, Unit]]( z => (a, b, c, d, e, f, g) => Z.dispatch(z(a, b, c, d, e, f, g)))( z => Reusable.byRef(z).withValue((a, b, c, d, e, f, g) => Z.delay(z(a, b, c, d, e, f, g)))) + implicit def ci7[A, B, C, D, E, F, G, Y, Z[_]](implicit Z: UnsafeSync[Z]): UseCallbackArg[(A, B, C, D, E, F, G) => Z[Y]] = + UseCallbackArg[(A, B, C, D, E, F, G) => Z[Y], js.Function7[A, B, C, D, E, F, G, Y]]( + z => (a, b, c, d, e, f, g) => Z.runSync(z(a, b, c, d, e, f, g)))( + z => Reusable.byRef(z).withValue((a, b, c, d, e, f, g) => Z.delay(z(a, b, c, d, e, f, g)))) + implicit def c8[A, B, C, D, E, F, G, H, Z[_]](implicit Z: Dispatch[Z]): UseCallbackArg[(A, B, C, D, E, F, G, H) => Z[Unit]] = UseCallbackArg[(A, B, C, D, E, F, G, H) => Z[Unit], js.Function8[A, B, C, D, E, F, G, H, Unit]]( z => (a, b, c, d, e, f, g, h) => Z.dispatch(z(a, b, c, d, e, f, g, h)))( z => Reusable.byRef(z).withValue((a, b, c, d, e, f, g, h) => Z.delay(z(a, b, c, d, e, f, g, h)))) + implicit def ci8[A, B, C, D, E, F, G, H, Y, Z[_]](implicit Z: UnsafeSync[Z]): UseCallbackArg[(A, B, C, D, E, F, G, H) => Z[Y]] = + UseCallbackArg[(A, B, C, D, E, F, G, H) => Z[Y], js.Function8[A, B, C, D, E, F, G, H, Y]]( + z => (a, b, c, d, e, f, g, h) => Z.runSync(z(a, b, c, d, e, f, g, h)))( + z => Reusable.byRef(z).withValue((a, b, c, d, e, f, g, h) => Z.delay(z(a, b, c, d, e, f, g, h)))) + implicit def c9[A, B, C, D, E, F, G, H, I, Z[_]](implicit Z: Dispatch[Z]): UseCallbackArg[(A, B, C, D, E, F, G, H, I) => Z[Unit]] = UseCallbackArg[(A, B, C, D, E, F, G, H, I) => Z[Unit], js.Function9[A, B, C, D, E, F, G, H, I, Unit]]( z => (a, b, c, d, e, f, g, h, i) => Z.dispatch(z(a, b, c, d, e, f, g, h, i)))( z => Reusable.byRef(z).withValue((a, b, c, d, e, f, g, h, i) => Z.delay(z(a, b, c, d, e, f, g, h, i)))) + implicit def ci9[A, B, C, D, E, F, G, H, I, Y, Z[_]](implicit Z: UnsafeSync[Z]): UseCallbackArg[(A, B, C, D, E, F, G, H, I) => Z[Y]] = + UseCallbackArg[(A, B, C, D, E, F, G, H, I) => Z[Y], js.Function9[A, B, C, D, E, F, G, H, I, Y]]( + z => (a, b, c, d, e, f, g, h, i) => Z.runSync(z(a, b, c, d, e, f, g, h, i)))( + z => Reusable.byRef(z).withValue((a, b, c, d, e, f, g, h, i) => Z.delay(z(a, b, c, d, e, f, g, h, i)))) + implicit def c10[A, B, C, D, E, F, G, H, I, J, Z[_]](implicit Z: Dispatch[Z]): UseCallbackArg[(A, B, C, D, E, F, G, H, I, J) => Z[Unit]] = UseCallbackArg[(A, B, C, D, E, F, G, H, I, J) => Z[Unit], js.Function10[A, B, C, D, E, F, G, H, I, J, Unit]]( z => (a, b, c, d, e, f, g, h, i, j) => Z.dispatch(z(a, b, c, d, e, f, g, h, i, j)))( z => Reusable.byRef(z).withValue((a, b, c, d, e, f, g, h, i, j) => Z.delay(z(a, b, c, d, e, f, g, h, i, j)))) + implicit def ci10[A, B, C, D, E, F, G, H, I, J, Y, Z[_]](implicit Z: UnsafeSync[Z]): UseCallbackArg[(A, B, C, D, E, F, G, H, I, J) => Z[Y]] = + UseCallbackArg[(A, B, C, D, E, F, G, H, I, J) => Z[Y], js.Function10[A, B, C, D, E, F, G, H, I, J, Y]]( + z => (a, b, c, d, e, f, g, h, i, j) => Z.runSync(z(a, b, c, d, e, f, g, h, i, j)))( + z => Reusable.byRef(z).withValue((a, b, c, d, e, f, g, h, i, j) => Z.delay(z(a, b, c, d, e, f, g, h, i, j)))) + implicit def c11[A, B, C, D, E, F, G, H, I, J, K, Z[_]](implicit Z: Dispatch[Z]): UseCallbackArg[(A, B, C, D, E, F, G, H, I, J, K) => Z[Unit]] = UseCallbackArg[(A, B, C, D, E, F, G, H, I, J, K) => Z[Unit], js.Function11[A, B, C, D, E, F, G, H, I, J, K, Unit]]( z => (a, b, c, d, e, f, g, h, i, j, k) => Z.dispatch(z(a, b, c, d, e, f, g, h, i, j, k)))( z => Reusable.byRef(z).withValue((a, b, c, d, e, f, g, h, i, j, k) => Z.delay(z(a, b, c, d, e, f, g, h, i, j, k)))) + implicit def ci11[A, B, C, D, E, F, G, H, I, J, K, Y, Z[_]](implicit Z: UnsafeSync[Z]): UseCallbackArg[(A, B, C, D, E, F, G, H, I, J, K) => Z[Y]] = + UseCallbackArg[(A, B, C, D, E, F, G, H, I, J, K) => Z[Y], js.Function11[A, B, C, D, E, F, G, H, I, J, K, Y]]( + z => (a, b, c, d, e, f, g, h, i, j, k) => Z.runSync(z(a, b, c, d, e, f, g, h, i, j, k)))( + z => Reusable.byRef(z).withValue((a, b, c, d, e, f, g, h, i, j, k) => Z.delay(z(a, b, c, d, e, f, g, h, i, j, k)))) + implicit def c12[A, B, C, D, E, F, G, H, I, J, K, L, Z[_]](implicit Z: Dispatch[Z]): UseCallbackArg[(A, B, C, D, E, F, G, H, I, J, K, L) => Z[Unit]] = UseCallbackArg[(A, B, C, D, E, F, G, H, I, J, K, L) => Z[Unit], js.Function12[A, B, C, D, E, F, G, H, I, J, K, L, Unit]]( z => (a, b, c, d, e, f, g, h, i, j, k, l) => Z.dispatch(z(a, b, c, d, e, f, g, h, i, j, k, l)))( z => Reusable.byRef(z).withValue((a, b, c, d, e, f, g, h, i, j, k, l) => Z.delay(z(a, b, c, d, e, f, g, h, i, j, k, l)))) + implicit def ci12[A, B, C, D, E, F, G, H, I, J, K, L, Y, Z[_]](implicit Z: UnsafeSync[Z]): UseCallbackArg[(A, B, C, D, E, F, G, H, I, J, K, L) => Z[Y]] = + UseCallbackArg[(A, B, C, D, E, F, G, H, I, J, K, L) => Z[Y], js.Function12[A, B, C, D, E, F, G, H, I, J, K, L, Y]]( + z => (a, b, c, d, e, f, g, h, i, j, k, l) => Z.runSync(z(a, b, c, d, e, f, g, h, i, j, k, l)))( + z => Reusable.byRef(z).withValue((a, b, c, d, e, f, g, h, i, j, k, l) => Z.delay(z(a, b, c, d, e, f, g, h, i, j, k, l)))) + implicit def c13[A, B, C, D, E, F, G, H, I, J, K, L, M, Z[_]](implicit Z: Dispatch[Z]): UseCallbackArg[(A, B, C, D, E, F, G, H, I, J, K, L, M) => Z[Unit]] = UseCallbackArg[(A, B, C, D, E, F, G, H, I, J, K, L, M) => Z[Unit], js.Function13[A, B, C, D, E, F, G, H, I, J, K, L, M, Unit]]( z => (a, b, c, d, e, f, g, h, i, j, k, l, m) => Z.dispatch(z(a, b, c, d, e, f, g, h, i, j, k, l, m)))( z => Reusable.byRef(z).withValue((a, b, c, d, e, f, g, h, i, j, k, l, m) => Z.delay(z(a, b, c, d, e, f, g, h, i, j, k, l, m)))) + implicit def ci13[A, B, C, D, E, F, G, H, I, J, K, L, M, Y, Z[_]](implicit Z: UnsafeSync[Z]): UseCallbackArg[(A, B, C, D, E, F, G, H, I, J, K, L, M) => Z[Y]] = + UseCallbackArg[(A, B, C, D, E, F, G, H, I, J, K, L, M) => Z[Y], js.Function13[A, B, C, D, E, F, G, H, I, J, K, L, M, Y]]( + z => (a, b, c, d, e, f, g, h, i, j, k, l, m) => Z.runSync(z(a, b, c, d, e, f, g, h, i, j, k, l, m)))( + z => Reusable.byRef(z).withValue((a, b, c, d, e, f, g, h, i, j, k, l, m) => Z.delay(z(a, b, c, d, e, f, g, h, i, j, k, l, m)))) + implicit def c14[A, B, C, D, E, F, G, H, I, J, K, L, M, N, Z[_]](implicit Z: Dispatch[Z]): UseCallbackArg[(A, B, C, D, E, F, G, H, I, J, K, L, M, N) => Z[Unit]] = UseCallbackArg[(A, B, C, D, E, F, G, H, I, J, K, L, M, N) => Z[Unit], js.Function14[A, B, C, D, E, F, G, H, I, J, K, L, M, N, Unit]]( z => (a, b, c, d, e, f, g, h, i, j, k, l, m, n) => Z.dispatch(z(a, b, c, d, e, f, g, h, i, j, k, l, m, n)))( z => Reusable.byRef(z).withValue((a, b, c, d, e, f, g, h, i, j, k, l, m, n) => Z.delay(z(a, b, c, d, e, f, g, h, i, j, k, l, m, n)))) + implicit def ci14[A, B, C, D, E, F, G, H, I, J, K, L, M, N, Y, Z[_]](implicit Z: UnsafeSync[Z]): UseCallbackArg[(A, B, C, D, E, F, G, H, I, J, K, L, M, N) => Z[Y]] = + UseCallbackArg[(A, B, C, D, E, F, G, H, I, J, K, L, M, N) => Z[Y], js.Function14[A, B, C, D, E, F, G, H, I, J, K, L, M, N, Y]]( + z => (a, b, c, d, e, f, g, h, i, j, k, l, m, n) => Z.runSync(z(a, b, c, d, e, f, g, h, i, j, k, l, m, n)))( + z => Reusable.byRef(z).withValue((a, b, c, d, e, f, g, h, i, j, k, l, m, n) => Z.delay(z(a, b, c, d, e, f, g, h, i, j, k, l, m, n)))) + implicit def c15[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, Z[_]](implicit Z: Dispatch[Z]): UseCallbackArg[(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O) => Z[Unit]] = UseCallbackArg[(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O) => Z[Unit], js.Function15[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, Unit]]( z => (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) => Z.dispatch(z(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o)))( z => Reusable.byRef(z).withValue((a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) => Z.delay(z(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o)))) + implicit def ci15[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, Y, Z[_]](implicit Z: UnsafeSync[Z]): UseCallbackArg[(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O) => Z[Y]] = + UseCallbackArg[(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O) => Z[Y], js.Function15[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, Y]]( + z => (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) => Z.runSync(z(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o)))( + z => Reusable.byRef(z).withValue((a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) => Z.delay(z(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o)))) + implicit def c16[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Z[_]](implicit Z: Dispatch[Z]): UseCallbackArg[(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P) => Z[Unit]] = UseCallbackArg[(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P) => Z[Unit], js.Function16[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Unit]]( z => (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) => Z.dispatch(z(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p)))( z => Reusable.byRef(z).withValue((a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) => Z.delay(z(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p)))) + implicit def ci16[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Y, Z[_]](implicit Z: UnsafeSync[Z]): UseCallbackArg[(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P) => Z[Y]] = + UseCallbackArg[(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P) => Z[Y], js.Function16[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Y]]( + z => (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) => Z.runSync(z(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p)))( + z => Reusable.byRef(z).withValue((a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) => Z.delay(z(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p)))) + implicit def c17[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, Z[_]](implicit Z: Dispatch[Z]): UseCallbackArg[(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q) => Z[Unit]] = UseCallbackArg[(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q) => Z[Unit], js.Function17[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, Unit]]( z => (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q) => Z.dispatch(z(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q)))( z => Reusable.byRef(z).withValue((a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q) => Z.delay(z(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q)))) + implicit def ci17[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, Y, Z[_]](implicit Z: UnsafeSync[Z]): UseCallbackArg[(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q) => Z[Y]] = + UseCallbackArg[(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q) => Z[Y], js.Function17[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, Y]]( + z => (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q) => Z.runSync(z(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q)))( + z => Reusable.byRef(z).withValue((a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q) => Z.delay(z(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q)))) + implicit def c18[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, Z[_]](implicit Z: Dispatch[Z]): UseCallbackArg[(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R) => Z[Unit]] = UseCallbackArg[(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R) => Z[Unit], js.Function18[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, Unit]]( z => (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r) => Z.dispatch(z(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r)))( z => Reusable.byRef(z).withValue((a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r) => Z.delay(z(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r)))) + implicit def ci18[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, Y, Z[_]](implicit Z: UnsafeSync[Z]): UseCallbackArg[(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R) => Z[Y]] = + UseCallbackArg[(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R) => Z[Y], js.Function18[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, Y]]( + z => (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r) => Z.runSync(z(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r)))( + z => Reusable.byRef(z).withValue((a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r) => Z.delay(z(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r)))) + implicit def c19[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, Z[_]](implicit Z: Dispatch[Z]): UseCallbackArg[(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S) => Z[Unit]] = UseCallbackArg[(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S) => Z[Unit], js.Function19[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, Unit]]( z => (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s) => Z.dispatch(z(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s)))( z => Reusable.byRef(z).withValue((a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s) => Z.delay(z(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s)))) + implicit def ci19[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, Y, Z[_]](implicit Z: UnsafeSync[Z]): UseCallbackArg[(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S) => Z[Y]] = + UseCallbackArg[(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S) => Z[Y], js.Function19[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, Y]]( + z => (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s) => Z.runSync(z(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s)))( + z => Reusable.byRef(z).withValue((a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s) => Z.delay(z(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s)))) + implicit def c20[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, Z[_]](implicit Z: Dispatch[Z]): UseCallbackArg[(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T) => Z[Unit]] = UseCallbackArg[(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T) => Z[Unit], js.Function20[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, Unit]]( z => (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t) => Z.dispatch(z(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t)))( z => Reusable.byRef(z).withValue((a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t) => Z.delay(z(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t)))) + implicit def ci20[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, Y, Z[_]](implicit Z: UnsafeSync[Z]): UseCallbackArg[(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T) => Z[Y]] = + UseCallbackArg[(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T) => Z[Y], js.Function20[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, Y]]( + z => (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t) => Z.runSync(z(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t)))( + z => Reusable.byRef(z).withValue((a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t) => Z.delay(z(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t)))) + implicit def c21[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, Z[_]](implicit Z: Dispatch[Z]): UseCallbackArg[(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U) => Z[Unit]] = UseCallbackArg[(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U) => Z[Unit], js.Function21[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, Unit]]( z => (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u) => Z.dispatch(z(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u)))( z => Reusable.byRef(z).withValue((a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u) => Z.delay(z(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u)))) + implicit def ci21[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, Y, Z[_]](implicit Z: UnsafeSync[Z]): UseCallbackArg[(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U) => Z[Y]] = + UseCallbackArg[(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U) => Z[Y], js.Function21[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, Y]]( + z => (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u) => Z.runSync(z(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u)))( + z => Reusable.byRef(z).withValue((a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u) => Z.delay(z(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u)))) + implicit def c22[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, Z[_]](implicit Z: Dispatch[Z]): UseCallbackArg[(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V) => Z[Unit]] = UseCallbackArg[(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V) => Z[Unit], js.Function22[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, Unit]]( z => (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v) => Z.dispatch(z(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v)))( z => Reusable.byRef(z).withValue((a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v) => Z.delay(z(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v)))) + implicit def ci22[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, Y, Z[_]](implicit Z: UnsafeSync[Z]): UseCallbackArg[(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V) => Z[Y]] = + UseCallbackArg[(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V) => Z[Y], js.Function22[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, Y]]( + z => (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v) => Z.runSync(z(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v)))( + z => Reusable.byRef(z).withValue((a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v) => Z.delay(z(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v)))) + } diff --git a/library/coreGeneric/src/main/scala/japgolly/scalajs/react/hooks/react17.scala b/library/coreGeneric/src/main/scala/japgolly/scalajs/react/hooks/react17.scala index 6920ea153..c934b154e 100644 --- a/library/coreGeneric/src/main/scala/japgolly/scalajs/react/hooks/react17.scala +++ b/library/coreGeneric/src/main/scala/japgolly/scalajs/react/hooks/react17.scala @@ -6,185 +6,185 @@ import japgolly.scalajs.react.{Reusability, _} trait react17 { /** - * Returns a memoized callback. - * - * Pass an inline callback and dependencies. useCallback will return a memoized version of the - * callback that only changes if one of the dependencies has changed. This is useful when passing - * callbacks to optimized child components that rely on reference equality to prevent unnecessary - * renders. - * - * @see - * https://reactjs.org/docs/hooks-reference.html#usecallback - */ + * Returns a memoized callback. + * + * Pass an inline callback and dependencies. useCallback will return a memoized version of the + * callback that only changes if one of the dependencies has changed. This is useful when passing + * callbacks to optimized child components that rely on reference equality to prevent unnecessary + * renders. + * + * @see + * https://reactjs.org/docs/hooks-reference.html#usecallback + */ @inline final def useCallback[A](callback: A)(implicit isCallbackArg: UseCallbackArg[A]): HookResult[Reusable[A]] = UseCallback(callback).toHookResult /** - * Returns a memoized callback. - * - * Pass an inline callback and dependencies. useCallback will return a memoized version of the - * callback that only changes if one of the dependencies has changed. This is useful when passing - * callbacks to optimized child components that rely on reference equality to prevent unnecessary - * renders. - * - * @see - * https://reactjs.org/docs/hooks-reference.html#usecallback - */ + * Returns a memoized callback. + * + * Pass an inline callback and dependencies. useCallback will return a memoized version of the + * callback that only changes if one of the dependencies has changed. This is useful when passing + * callbacks to optimized child components that rely on reference equality to prevent unnecessary + * renders. + * + * @see + * https://reactjs.org/docs/hooks-reference.html#usecallback + */ @inline final def useCallbackWithDeps[D: Reusability, A](deps: => D)(callback: D => A)( implicit isCallbackArg: UseCallbackArg[A] ): HookResult[Reusable[A]] = UseCallback.withDeps(deps)(callback).toHookResult /** - * Accepts a context object and returns the current context value for that context. The current - * context value is determined by the value prop of the nearest `` above the - * calling component in the tree. - * - * When the nearest `` above the component updates, this Hook will trigger a - * rerender with the latest context value passed to that `MyContext` provider. Even if an ancestor - * uses `React.memo` or `shouldComponentUpdate`, a rerender will still happen starting at the - * component itself using `useContext`. - * - * A component calling `useContext` will always re-render when the context value changes. If - * re-rendering the component is expensive, you can optimize it by using memoization. - * - * `useContext(MyContext)` only lets you read the context and subscribe to its changes. You still - * need a `` above in the tree to provide the value for this context. - * - * @see - * https://reactjs.org/docs/hooks-reference.html#usecontext - */ + * Accepts a context object and returns the current context value for that context. The current + * context value is determined by the value prop of the nearest `` above the + * calling component in the tree. + * + * When the nearest `` above the component updates, this Hook will trigger a + * rerender with the latest context value passed to that `MyContext` provider. Even if an ancestor + * uses `React.memo` or `shouldComponentUpdate`, a rerender will still happen starting at the + * component itself using `useContext`. + * + * A component calling `useContext` will always re-render when the context value changes. If + * re-rendering the component is expensive, you can optimize it by using memoization. + * + * `useContext(MyContext)` only lets you read the context and subscribe to its changes. You still + * need a `` above in the tree to provide the value for this context. + * + * @see + * https://reactjs.org/docs/hooks-reference.html#usecontext + */ @inline final def useContext[A](ctx: Context[A]): HookResult[A] = HookResult(UseContext.unsafeCreate(ctx)) /** - * Used to display a label for custom hooks in React DevTools. - * - * @see - * https://reactjs.org/docs/hooks-reference.html#usedebugvalue - */ + * Used to display a label for custom hooks in React DevTools. + * + * @see + * https://reactjs.org/docs/hooks-reference.html#usedebugvalue + */ @inline final def useDebugValue(desc: => Any): HookResult[Unit] = HookResult(UseDebugValue.unsafeCreate(desc)) /** - * The callback passed to useEffect will run after the render is committed to the screen. Think of - * effects as an escape hatch from React’s purely functional world into the imperative world. - * - * By default, effects run after every completed render. If you'd only like to execute the effect - * when your component is mounted, then use [[useEffectOnMount]]. If you'd only like to execute the - * effect when certain values have changed, provide those certain values as the second argument. - * - * @see - * https://reactjs.org/docs/hooks-reference.html#useeffect - */ + * The callback passed to useEffect will run after the render is committed to the screen. Think of + * effects as an escape hatch from React’s purely functional world into the imperative world. + * + * By default, effects run after every completed render. If you'd only like to execute the effect + * when your component is mounted, then use [[useEffectOnMount]]. If you'd only like to execute the + * effect when certain values have changed, then use [[useEffectWithDeps]]. + * + * @see + * https://reactjs.org/docs/hooks-reference.html#useeffect + */ @inline final def useEffect[A](effect: A)(implicit isEffectArg: UseEffectArg[A]): HookResult[Unit] = HookResult(UseEffect.unsafeCreate(effect)) /** - * The callback passed to useEffect will run after the render is committed to the screen. Think of - * effects as an escape hatch from React’s purely functional world into the imperative world. - * - * This will only execute the effect when your component is mounted. - * - * @see - * https://reactjs.org/docs/hooks-reference.html#useeffect - */ + * The callback passed to useEffect will run after the render is committed to the screen. Think of + * effects as an escape hatch from React’s purely functional world into the imperative world. + * + * This will only execute the effect when your component is mounted. + * + * @see + * https://reactjs.org/docs/hooks-reference.html#useeffect + */ @inline final def useEffectOnMount[A](effect: A)(implicit isEffectArg: UseEffectArg[A]): HookResult[Unit] = HookResult(UseEffect.unsafeCreateOnMount(effect)) /** - * The callback passed to useEffect will run after the render is committed to the screen. Think of - * effects as an escape hatch from React’s purely functional world into the imperative world. - * - * This will only execute the effect when values in the second argument, change. - * - * @see - * https://reactjs.org/docs/hooks-reference.html#useeffect - */ + * The callback passed to useEffect will run after the render is committed to the screen. Think of + * effects as an escape hatch from React’s purely functional world into the imperative world. + * + * This will only execute the effect when values in the first argument change. + * + * @see + * https://reactjs.org/docs/hooks-reference.html#useeffect + */ @inline final def useEffectWithDeps[D: Reusability, A](deps: => D)(effect: D => A)( implicit isEffectArg: UseEffectArg[A] ): HookResult[Unit] = ReusableEffect.useEffect(deps)(effect).toHookResult /** - * The signature is identical to [[useEffect]], but it fires synchronously after all DOM mutations. - * Use this to read layout from the DOM and synchronously re-render. Updates scheduled inside - * useLayoutEffect will be flushed synchronously, before the browser has a chance to paint. - * - * Prefer the standard [[useEffect]] when possible to avoid blocking visual updates. - * - * If you'd only like to execute the effect when your component is mounted, then use - * [[useLayoutEffectOnMount]]. If you'd only like to execute the effect when certain values have - * changed, provide those certain values as the second argument. - * - * @see - * https://reactjs.org/docs/hooks-reference.html#useLayoutEffect - */ + * The signature is identical to [[useEffect]], but it fires synchronously after all DOM mutations. + * Use this to read layout from the DOM and synchronously re-render. Updates scheduled inside + * useLayoutEffect will be flushed synchronously, before the browser has a chance to paint. + * + * Prefer the standard [[useEffect]] when possible to avoid blocking visual updates. + * + * If you'd only like to execute the effect when your component is mounted, then use + * [[useLayoutEffectOnMount]]. If you'd only like to execute the effect when certain values have + * changed, then use [[useLayoutEffectWithDeps]]. + * + * @see + * https://reactjs.org/docs/hooks-reference.html#useLayoutEffect + */ @inline final def useLayoutEffect[A](effect: A)(implicit isEffectArg: UseEffectArg[A]): HookResult[Unit] = HookResult(UseEffect.unsafeCreateLayout(effect)) /** - * The signature is identical to [[useEffect]], but it fires synchronously after all DOM mutations. - * Use this to read layout from the DOM and synchronously re-render. Updates scheduled inside - * useLayoutEffect will be flushed synchronously, before the browser has a chance to paint. - * - * Prefer the standard [[useEffect]] when possible to avoid blocking visual updates. - * - * This will only execute the effect when your component is mounted. - * - * @see - * https://reactjs.org/docs/hooks-reference.html#useLayoutEffect - */ + * The signature is identical to [[useEffect]], but it fires synchronously after all DOM mutations. + * Use this to read layout from the DOM and synchronously re-render. Updates scheduled inside + * useLayoutEffect will be flushed synchronously, before the browser has a chance to paint. + * + * Prefer the standard [[useEffect]] when possible to avoid blocking visual updates. + * + * This will only execute the effect when your component is mounted. + * + * @see + * https://reactjs.org/docs/hooks-reference.html#useLayoutEffect + */ @inline final def useLayoutEffectOnMount[A](effect: A)(implicit isEffectArg: UseEffectArg[A]): HookResult[Unit] = HookResult(UseEffect.unsafeCreateLayoutOnMount(effect)) /** - * The signature is identical to [[useEffect]], but it fires synchronously after all DOM mutations. - * Use this to read layout from the DOM and synchronously re-render. Updates scheduled inside - * useLayoutEffect will be flushed synchronously, before the browser has a chance to paint. - * - * Prefer the standard [[useEffect]] when possible to avoid blocking visual updates. - * - * This will only execute the effect when values in the second argument, change. - * - * @see - * https://reactjs.org/docs/hooks-reference.html#useLayoutEffect - */ + * The signature is identical to [[useEffect]], but it fires synchronously after all DOM mutations. + * Use this to read layout from the DOM and synchronously re-render. Updates scheduled inside + * useLayoutEffect will be flushed synchronously, before the browser has a chance to paint. + * + * Prefer the standard [[useEffect]] when possible to avoid blocking visual updates. + * + * This will only execute the effect when values in the first argument change. + * + * @see + * https://reactjs.org/docs/hooks-reference.html#useLayoutEffect + */ @inline final def useLayoutEffectWithDeps[D: Reusability, A](deps: => D)(effect: D => A)( implicit isEffectArg: UseEffectArg[A] ): HookResult[Unit] = ReusableEffect.useLayoutEffect(deps)(effect).toHookResult /** - * Returns a memoized value. - * - * Pass a “create” function and any dependencies. useMemo will only recompute the memoized value - * when one of the dependencies has changed. This optimization helps to avoid expensive calculations - * on every render. - * - * Remember that the function passed to useMemo runs during rendering. Don’t do anything there that - * you wouldn’t normally do while rendering. For example, side effects belong in [[useEffect]], not - * useMemo. - * - * @see - * https://reactjs.org/docs/hooks-reference.html#usememo - */ + * Returns a memoized value. + * + * Pass a “create” function and any dependencies. useMemo will only recompute the memoized value + * when one of the dependencies has changed. This optimization helps to avoid expensive calculations + * on every render. + * + * Remember that the function passed to useMemo runs during rendering. Don’t do anything there that + * you wouldn’t normally do while rendering. For example, side effects belong in [[useEffect]], not + * useMemo. + * + * @see + * https://reactjs.org/docs/hooks-reference.html#usememo + */ @inline final def useMemo[D: Reusability, A](deps: => D)(create: D => A): HookResult[Reusable[A]] = UseMemo(deps)(create).toHookResult /** - * An alternative to [[useState]]. Accepts a reducer of type `(state, action) => newState`, and - * returns the current state paired with a dispatch method. (If you’re familiar with Redux, you - * already know how this works.) - * - * useReducer is usually preferable to useState when you have complex state logic that involves - * multiple sub-values or when the next state depends on the previous one. useReducer also lets you - * optimize performance for components that trigger deep updates because you can pass dispatch down - * instead of callbacks. - * - * @see - * https://reactjs.org/docs/hooks-reference.html#usereducer - */ + * An alternative to [[useState]]. Accepts a reducer of type `(state, action) => newState`, and + * returns the current state paired with a dispatch method. (If you’re familiar with Redux, you + * already know how this works.) + * + * useReducer is usually preferable to useState when you have complex state logic that involves + * multiple sub-values or when the next state depends on the previous one. useReducer also lets you + * optimize performance for components that trigger deep updates because you can pass dispatch down + * instead of callbacks. + * + * @see + * https://reactjs.org/docs/hooks-reference.html#usereducer + */ @inline final def useReducer[S, A](reducer: (S, A) => S, initialState: => S): HookResult[UseReducer[S, A]] = HookResult(UseReducer.unsafeCreate(reducer, initialState)) @@ -193,14 +193,14 @@ trait react17 { HookResult(UseRef.unsafeCreate(initialValue)) /** - * Returns a stateful value, and a function to update it. - * - * During the initial render, the returned state is the same as the value passed as the first - * argument (initialState). - * - * During subsequent re-renders, the first value returned by useState will always be the most recent - * state after applying updates. - */ + * Returns a stateful value, and a function to update it. + * + * During the initial render, the returned state is the same as the value passed as the first + * argument (initialState). + * + * During subsequent re-renders, the first value returned by useState will always be the most recent + * state after applying updates. + */ @inline final def useState[A](initial: => A): HookResult[UseState[A]] = HookResult(UseState.unsafeCreate(initial)) } \ No newline at end of file diff --git a/library/coreGeneric/src/main/scala/japgolly/scalajs/react/hooks/react18.scala b/library/coreGeneric/src/main/scala/japgolly/scalajs/react/hooks/react18.scala index 9ea56473b..a3ab3f5d2 100644 --- a/library/coreGeneric/src/main/scala/japgolly/scalajs/react/hooks/react18.scala +++ b/library/coreGeneric/src/main/scala/japgolly/scalajs/react/hooks/react18.scala @@ -1,10 +1,14 @@ package japgolly.scalajs.react.hooks +import japgolly.scalajs.react.Reusability +import japgolly.scalajs.react.hooks.HookResult import japgolly.scalajs.react.hooks.Hooks._ +import japgolly.scalajs.react.util.Effect.Sync +import scala.scalajs.js trait react18 { /** - * `useId` is a React Hook for generating unique IDs that can be passed to accessibility attributes. + * Generates unique IDs that can be passed to accessibility attributes. * * @see * https://react.dev/reference/react/useId @@ -25,4 +29,72 @@ trait react18 { */ @inline final def useTransition: HookResult[UseTransition] = UseTransition().toHookResult + + /** + * Lets you subscribe to an external store. + * + * @see + * {@link https://react.dev/reference/react/useSyncExternalStore} + */ + @inline final def useSyncExternalStore[F[_], A]( + subscribe: F[Unit] => F[F[Unit]], + getSnapshot: F[A], + getServerSnapshot: js.UndefOr[F[A]] = js.undefined + )(implicit F: Sync[F]): HookResult[A] = + UseSyncExternalStore(subscribe, getSnapshot, getServerSnapshot).toHookResult + + + /** The signature is identical to [[useEffect]], but it fires synchronously after all DOM mutations, but before any + * layout Effects fire. Use this to insert styles before any Effects fire that may need to read layout. Updates + * scheduled inside useLayoutEffect will be flushed synchronously, before the browser has a chance to paint. + * + * Prefer the standard [[useEffect]] when possible to avoid blocking visual updates. + * + * If you'd only like to execute the effect when your component is mounted, then use [[useInsertionEffectOnMount]]. + * If you'd only like to execute the effect when certain values have changed, then use [[useInsertionEffectWithDeps]]. + * + * @see https://react.dev/reference/react/useInsertionEffect#useInsertionEffect + */ + @inline final def useInsertionEffect[A](effect: A)(implicit isEffectArg: UseEffectArg[A]): HookResult[Unit] = + HookResult(UseEffect.unsafeCreateInsertion(effect)) + + /** The signature is identical to [[useEffect]], but it fires synchronously after all DOM mutations, but before any + * layout Effects fire. Use this to insert styles before any Effects fire that may need to read layout. Updates + * scheduled inside useLayoutEffect will be flushed synchronously, before the browser has a chance to paint. + * + * Prefer the standard [[useEffect]] when possible to avoid blocking visual updates. + * + * This will only execute the effect when your component is mounted. + * + * @see https://react.dev/reference/react/useInsertionEffect#useInsertionEffect + */ + @inline final def useInsertionEffectOnMount[A](effect: A)(implicit isEffectArg: UseEffectArg[A]): HookResult[Unit] = + HookResult(UseEffect.unsafeCreateInsertionOnMount(effect)) + + /** The signature is identical to [[useEffect]], but it fires synchronously after all DOM mutations, but before any + * layout Effects fire. Use this to insert styles before any Effects fire that may need to read layout. Updates + * scheduled inside useLayoutEffect will be flushed synchronously, before the browser has a chance to paint. + * + * Prefer the standard [[useEffect]] when possible to avoid blocking visual updates. + * + * This will only execute the effect when values in the first argument change. + * + * @see https://react.dev/reference/react/useInsertionEffect#useInsertionEffect + */ + @inline final def useInsertionEffectWithDeps[D: Reusability, A](deps: => D)(effect: D => A)( + implicit isEffectArg: UseEffectArg[A] + ): HookResult[Unit] = + ReusableEffect.useInsertionEffect(deps)(effect).toHookResult + + /** + * Lets you defer updating a part of the UI. + * + * @see + * {@link https://react.dev/reference/react/useDeferredValue} + */ + // initialValue was added in React 19 - Replace when we upgrade to React 19 + // @inline final def useDeferredValue[A](value: A, initialValue: js.UndefOr[A] = js.undefined): HookResult[A] = + // UseDeferredValue(value, initialValue).toHookResult + @inline final def useDeferredValue[A](value: A): HookResult[A] = + UseDeferredValue(value).toHookResult } \ No newline at end of file diff --git a/library/facadeMain/src/main/scala/japgolly/scalajs/react/facade/Hooks.scala b/library/facadeMain/src/main/scala/japgolly/scalajs/react/facade/Hooks.scala index ba1e822fd..1d47b5087 100644 --- a/library/facadeMain/src/main/scala/japgolly/scalajs/react/facade/Hooks.scala +++ b/library/facadeMain/src/main/scala/japgolly/scalajs/react/facade/Hooks.scala @@ -1,6 +1,5 @@ package japgolly.scalajs.react.facade -import scala.annotation.nowarn import scala.scalajs.js import scala.scalajs.js.| @@ -9,7 +8,6 @@ import scala.scalajs.js.| * @since React 16.8.0 / scalajs-react 2.0.0 */ @js.native -@nowarn("cat=unused") trait Hooks extends js.Object { final type HookDeps = js.UndefOr[js.Array[_]] | Null @@ -28,6 +26,9 @@ trait Hooks extends js.Object { final def useLayoutEffect(effect: js.Function0[js.UndefOr[js.Function0[Any]]], deps : js.UndefOr[HookDeps] = js.native): Unit = js.native + final def useInsertionEffect(effect: js.Function0[js.UndefOr[js.Function0[Any]]], + deps : js.UndefOr[HookDeps] = js.native): Unit = js.native + final def useContext[A](ctx: React.Context[A]): A = js.native final type UseReducerDispatch[-A] = js.Function1[A, Unit] @@ -52,4 +53,15 @@ trait Hooks extends js.Object { final def useId(): String = js.native final def useTransition(): UseTransition = js.native + + final type UseSyncExternalStoreSubscribeArg = js.Function1[js.Function0[Unit], js.Function0[Unit]] + final def useSyncExternalStore[A]( + subscribe: UseSyncExternalStoreSubscribeArg, + getSnapshot: js.Function0[A], + getServerSnapshot: js.UndefOr[js.Function0[A]] = js.undefined + ): A = js.native + + // initialValue was added in React 19 - Replace when we upgrade to React 19 + // final def useDeferredValue[A](value: A, initialValue: js.UndefOr[A] = js.undefined): A = js.native + final def useDeferredValue[A](value: A): A = js.native } diff --git a/library/facadeMain/src/main/scala/japgolly/scalajs/react/facade/React.scala b/library/facadeMain/src/main/scala/japgolly/scalajs/react/facade/React.scala index c6f30aba5..c54bfe3b6 100644 --- a/library/facadeMain/src/main/scala/japgolly/scalajs/react/facade/React.scala +++ b/library/facadeMain/src/main/scala/japgolly/scalajs/react/facade/React.scala @@ -180,7 +180,6 @@ object React extends React { } @js.native -@nowarn("cat=unused") trait React extends Hooks { import React._ diff --git a/library/project/GenHooks.scala b/library/project/GenHooks.scala index a7b4ae1ec..1f8029cf2 100644 --- a/library/project/GenHooks.scala +++ b/library/project/GenHooks.scala @@ -58,6 +58,13 @@ object GenHooks { | z => ($as) => Z.dispatch(z($as)))( | z => Reusable.byRef(z).withValue(($as) => Z.delay(z($as)))) |""".stripMargin + + useCallbackArgs += + s""" implicit def ci$n[$As, Y, Z[_]](implicit Z: UnsafeSync[Z]): UseCallbackArg[($As) => Z[Y]] = + | UseCallbackArg[($As) => Z[Y], js.Function$n[$As, Y]]( + | z => ($as) => Z.runSync(z($as)))( + | z => Reusable.byRef(z).withValue(($as) => Z.delay(z($as)))) + |""".stripMargin if (n <= 21) { hookCtxCtorsI += s" def apply[I, $Hns](input: I, $hookParams): I$n[I, $Hns] =\n new I$n(input, $hookArgs)" diff --git a/library/tests/src/test/scala/japgolly/scalajs/react/core/HooksTest.scala b/library/tests/src/test/scala/japgolly/scalajs/react/core/HooksTest.scala index 8279e279c..50e30baea 100644 --- a/library/tests/src/test/scala/japgolly/scalajs/react/core/HooksTest.scala +++ b/library/tests/src/test/scala/japgolly/scalajs/react/core/HooksTest.scala @@ -9,6 +9,7 @@ import japgolly.scalajs.react.test.ReactTestUtils._ import japgolly.scalajs.react.test.TestUtil._ import japgolly.scalajs.react.vdom.html_<^._ import org.scalajs.dom.html.Input +import scala.collection.mutable import scala.scalajs.js import utest._ @@ -718,6 +719,35 @@ object HooksTest extends TestSuite { } } + private object UseInsertionEffect extends UseEffectTests { + override protected implicit def hooksExt1[Ctx, Step <: HooksApi.AbstractStep](api: HooksApi.Primary[Ctx, Step]): Primary[Ctx, Step] = + new Primary(api) + override protected implicit def hooksExt2[Ctx, CtxFn[_], Step <: HooksApi.SubsequentStep[Ctx, CtxFn]](api: HooksApi.Secondary[Ctx, CtxFn, Step]): Secondary[Ctx, CtxFn, Step] = + new Secondary(api) + protected class Primary[Ctx, Step <: HooksApi.AbstractStep](api: HooksApi.Primary[Ctx, Step]) extends X_UseEffect_Primary[Ctx, Step] { + override def X_useEffect[A](effect: A)(implicit a: UseEffectArg[A], step: Step) = + api.useInsertionEffect(effect) + override def X_useEffectBy[A](init: Ctx => A)(implicit a: UseEffectArg[A], step: Step) = + api.useInsertionEffectBy(init) + override def X_useEffectOnMount[A](effect: A)(implicit a: UseEffectArg[A], step: Step) = + api.useInsertionEffectOnMount(effect) + override def X_useEffectOnMountBy[A](effect: Ctx => A)(implicit a: UseEffectArg[A], step: Step) = + api.useInsertionEffectOnMountBy(effect) + override def X_useEffectWithDeps[D, A](deps: => D)(effect: D => A)(implicit a: UseEffectArg[A], r: Reusability[D], step: Step) = + api.useInsertionEffectWithDeps(deps)(effect) + override def X_useEffectWithDepsBy[D, A](deps: Ctx => D)(effect: Ctx => D => A)(implicit a: UseEffectArg[A], r: Reusability[D], step: Step) = + api.useInsertionEffectWithDepsBy(deps)(effect) + } + protected class Secondary[Ctx, CtxFn[_], Step <: HooksApi.SubsequentStep[Ctx, CtxFn]](api: HooksApi.Secondary[Ctx, CtxFn, Step]) extends Primary(api) with X_UseEffect_Secondary[Ctx, CtxFn, Step] { + override def X_useEffectBy[A](init: CtxFn[A])(implicit a: UseEffectArg[A], step: Step): step.Self = + api.useInsertionEffectBy(init) + override def X_useEffectOnMountBy[A](effect: CtxFn[A])(implicit a: UseEffectArg[A], step: Step): step.Self = + api.useInsertionEffectOnMountBy(effect) + override def X_useEffectWithDepsBy[D, A](deps: CtxFn[D])(effect: CtxFn[D => A])(implicit a: UseEffectArg[A], r: Reusability[D], step: Step): step.Self = + api.useInsertionEffectWithDepsBy(deps)(effect) + } + } + private abstract class UseEffectTests { protected implicit def hooksExt1[Ctx, Step <: HooksApi.AbstractStep](api: HooksApi.Primary[Ctx, Step]): X_UseEffect_Primary[Ctx, Step] protected implicit def hooksExt2[Ctx, CtxFn[_], Step <: HooksApi.SubsequentStep[Ctx, CtxFn]](api: HooksApi.Secondary[Ctx, CtxFn, Step]): X_UseEffect_Secondary[Ctx, CtxFn, Step] @@ -983,7 +1013,16 @@ object HooksTest extends TestSuite { useLayoutEffectOnMount(effect) protected def X_useEffectWithDeps[D, A](deps: => D)(effect: D => A)(implicit a: UseEffectArg[A], r: Reusability[D]): HookResult[Unit] = useLayoutEffectWithDeps(deps)(effect) - } + } + + private object UseInsertionEffectMonadic extends UseEffectHub { + protected def X_useEffect[A](effect: A)(implicit a: UseEffectArg[A]): HookResult[Unit] = + useInsertionEffect(effect) + protected def X_useEffectOnMount[A](effect: A)(implicit a: UseEffectArg[A]): HookResult[Unit] = + useInsertionEffectOnMount(effect) + protected def X_useEffectWithDeps[D, A](deps: => D)(effect: D => A)(implicit a: UseEffectArg[A], r: Reusability[D]): HookResult[Unit] = + useInsertionEffectWithDeps(deps)(effect) + } private def testUseForceUpdate(): Unit = { val counter = new Counter @@ -2104,6 +2143,146 @@ object HooksTest extends TestSuite { } } + object UseSyncExternalStore { + private class ExternalStore { + private val values: mutable.Map[Boolean, Int] = mutable.Map(true -> 0, false -> 0) + private val listeners: mutable.Map[Boolean, Callback] = mutable.Map.empty + + def get(which: Boolean): CallbackTo[Int] = CallbackTo(values(which)) + + def register(which: Boolean)(listener: Callback): CallbackTo[Callback] = { + Callback(this.listeners.updateWith(which)(_ => Some(listener))) + .ret(Callback(this.listeners.updateWith(which)(_ => None))) + } + + def peekListener(which: Boolean): Option[Callback] = listeners.get(which) + + def notifyListener(which: Boolean): Callback = listeners.get(which).getOrElse(Callback.empty) + + def inc(which: Boolean): Callback = Callback(values.updateWith(which)(_.map(_ + 1))) >> notifyListener(which) + } + + def testConst() = { + val store = new ExternalStore + + val comp = ScalaFnComponent + .withHooks[Unit] + .useSyncExternalStore(store.register(true), store.get(true)) + .render { (_, i) => + <.div(s"i=$i") + } + + test(comp()) { t => + t.assertText("i=0") + store.inc(true).runNow() + t.assertText("i=1") + } + assert(store.peekListener(true).isEmpty) + assert(store.peekListener(false).isEmpty) + } + + def testConstBy() = { + val store = new ExternalStore + + val comp = ScalaFnComponent + .withHooks[Boolean] + .useSyncExternalStoreBy(store.register, store.get) + .render { (_, i) => + <.div(s"i=$i") + } + + test(comp(false)) { t => + t.assertText("i=0") + store.inc(false).runNow() + t.assertText("i=1") + } + assert(store.peekListener(true).isEmpty) + assert(store.peekListener(false).isEmpty) + } + + def testMonadicConst() = { + val store = new ExternalStore + + val comp = ScalaFnComponent[Unit] { _ => + for { + i <- useSyncExternalStore(store.register(true), store.get(true)) + } yield <.div(s"i=$i") + } + + test(comp()) { t => + t.assertText("i=0") + store.inc(true).runNow() + t.assertText("i=1") + } + assert(store.peekListener(true).isEmpty) + assert(store.peekListener(false).isEmpty) + } + } + + object UseDeferred { + def testConst() = { + var renders: List[(Int, Int, Boolean)] = Nil + + val comp = ScalaFnComponent + .withHooks[Unit] + .useState(0) + .useDeferredValue((_, state) => state.value) + .render { (_, state, deferredValue) => + val isStale: Boolean = state.value != deferredValue + renders = renders :+ (state.value, deferredValue, isStale) + <.button(^.onClick --> state.modState(_ + 1)) + } + + test(comp()) { t => + t.clickButton() + } + assertEq(renders, List((0, 0, false), (1, 0, true), (1, 1, false))) + } + + // initialValue was added in React 19 - Uncomment when we upgrade to React 19 + // def testConstWithInitial() = { + // var renders: List[(Int, Int, Boolean)] = Nil + + // val comp = ScalaFnComponent + // .withHooks[Unit] + // .useState(0) + // .useDeferredValue((_, state) => state.value, (_, _) => 100) + // .render { (_, state, deferredValue) => + // val isStale: Boolean = state.value != deferredValue + // renders = renders :+ (state.value, deferredValue, isStale) + // <.div( + // deferredValue, + // <.button(^.onClick --> state.modState(_ + 1)) + // ) + // } + + // test(comp()) { t => + // t.clickButton() + // } + // assertEq(renders, List((0, 100, true), (0, 0, false), (1, 0, true), (1, 1, false))) + // } + + def testMonadicConst() = { + var renders: List[(Int, Int, Boolean)] = Nil + + val comp = ScalaFnComponent[Unit] { _ => + for { + state <- useState(0) + deferredValue <- useDeferredValue(state.value) + } yield { + val isStale: Boolean = state.value != deferredValue + renders = renders :+ (state.value, deferredValue, isStale) + <.button(^.onClick --> state.modState(_ + 1)) + } + } + + test(comp()) { t => + t.clickButton() + } + assertEq(renders, List((0, 0, false), (1, 0, true), (1, 1, false))) + } + } + // =================================================================================================================== override def tests = Tests { @@ -2165,7 +2344,24 @@ object HooksTest extends TestSuite { "const" - testConst() "depsBy" - testWithDeps() "mount" - testOnMount() - } + } + "useInsertionEffect" - { + import UseInsertionEffect._ + "single" - testSingle() + "const" - testConst() + "constBy" - testConstBy() + "deps" - testWithDeps() + "depsBy" - testWithDepsBy() + "mount" - testOnMount() + "mountBy" - testOnMountBy() + } + "useInsertionEffect (monadic)" - { + import UseInsertionEffectMonadic._ + "single" - testSingle() + "const" - testConst() + "depsBy" - testWithDeps() + "mount" - testOnMount() + } "useMemo" - { "deps" - testUseMemo() "depsBy" - testUseMemoBy() @@ -2223,6 +2419,24 @@ object HooksTest extends TestSuite { "useTransition" - testUseTransition() "useTransition (monadic)" - testMonadicUseTransition() + "useSyncExternalStore" - { + "const" - UseSyncExternalStore.testConst() + "constBy" - UseSyncExternalStore.testConstBy() + } + "useSyncExternalStore (monadic)" - { + "const" - UseSyncExternalStore.testMonadicConst() + } + + "useDeferred" - { + "const" - UseDeferred.testConst() + // initialValue was added in React 19 - Uncomment when we upgrade to React 19 + // "constWithInitial" - UseDeferred.testConstWithInitial() + } + "useDeferred (monadic)" - { + "const" - UseDeferred.testMonadicConst() + // "constWithInitial" - UseDeferred.testMonadicConstWithInitial() + } + "renderWithReuse" - { "main" - testRenderWithReuse() "never" - testRenderWithReuseNever()