This is a big release with big release notes. Grab a coffee and enjoy!
Firstly: A big chunk of this work was kindly sponsored by Gemini/NOIRLab/AURA c/o @cquiroz. A big thank you from myself and the community!
-
Scala.JS 1.0 support. We cross-build to retain support of Scala.JS 0.6.x. This will continue until the first upstream dependency drops support for 0.6.x.
-
Upgrade to React 16.13.1
- Add
ReactDOM.version
- Add
ReactDOMServer.version
- Add
ReactTestUtils.act(body)
- Add
ReactTestUtils.actAsync(body)
- Add
React.Profiler(id, callback)(children)
-- see theReact.Profiler
doc for detail - Add
React.Profiler.unstable_trace(name)(body)
- New HTML attribute:
disablePictureInPicture
- Deprecated
componentWillMount
- Deprecated
componentWillReceiveProps
- Deprecated
componentWillUpdate
- Add
-
React support
- Add
code: String
to keyboard events - Added
ReactFormEvent
. It currently adds nothing over a basic event but it aligns with React's doc and they might decide to specialise it in future. ReactDOMServer.render
methods andReactDOM.hydrate
now acceptVdomNode
s instead ofVdomElement
s
- Add
-
Introduction of
ScalaJsReactConfig
and the concept of global settings. Inside isDevOnly
which houses settings that only affectfastOptJS
and are removed fromfullOptJS
at compile-time. -
The very first global dev-only setting is
reusabilityOverride
which allows you to override the behaviour ofReusability.shouldComponentUpdate
. The most common use case is that during development you can callReusabilityOverlay.overrideGloballyInDev()
at the start of your JSmain
method which will provide a little GUI around all components withReusability.shouldComponentUpdate
that shows you whenReusability
prevented updates, when it re-rendered, and why (either by hovering over the overlay, or clicking it to get a full log in the console). You can also callScalaJsReactConfig.DevOnly.overrideReusability
yourself and provide your own implementation if desirable. -
Component names can now be generated automatically. You no longer need to specify them manually when creating a component. If you'd like to change all of your existing components to do this, there's a migration at the end of this page.
For example the following component
package com.github.example object Blah { val TopLevelComponent = ScalaComponent.builder[Unit] .renderBackend[Backend] .build }
would be automatically named
"com.github.example.Blah.TopLevel"
. -
Automatic component naming trims
"Comp"
and"Component"
from names by default. This behaviour can be overriden by calling thecomponentNameModifier*
methods onScalaJsReactConfig
.For example, you might want to call
ScalaJsReactConfig.componentNameModifierAppend(_.stripPrefix("com.github.example."))
to trim package names, resulting in the previous example being called"Blah.TopLevel"
. -
Component names are now optionally elidable. Good for production builds.
Specifically, if you add
scalacOptions ++= Seq("-Xelide-below", "OFF")
to your SBT, all of your component names will be removed from your output JS. -
In the component builder
-
instead of calling
initialState*
, you can now callgetDerivedStateFromProps[S](f: P => S)
orgetDerivedStateFromPropsAndState[S](f: (P, Option[S]) => S)
(To be clear: you don't need to callgetDerivedStateFromProps
again later.)val Example = ScalaComponent.builder[Props] .getDerivedStateFromProps(_.text) .renderBackend[Backend] .build
-
renamed
getDerivedStateFromProps(? => Option[S])
togetDerivedStateFromPropsOption
-
added
getDerivedStateFromProps
variant which accepts a? => S
-
bugfix: calling
getDerivedStateFromProps
multiple times used to just take the firstSome[S]
result and discard subsequent calculations which is pretty silly in retrospect. It now runs them all sequentially chaining state updates of one, to the next.
-
-
VDOM changes
- added
VdomNode.static(vdom: VdomNode): VdomNode
that wraps the givenvdom
in a component that always returnsfalse
fromshouldComponentUpdate
- added
VdomElement.static(vdom: VdomElement): VdomElement
that wraps the givenvdom
in a component that always returnsfalse
fromshouldComponentUpdate
- added
.withOptionalRef
that takes anOption
, everywhere that.withRef
exists
- added
-
Bugfix:
setState(newState: S, callback: Callback)
wasn't calling the specifiedCallback
(!!!) -
Added async versions of all
{set,mod}State
functions. These returnAsyncCallback
s that resume when React has finished applying the state change..setStateAsync(S) : AsyncCallback[Unit] .modStateAsync(S => S) : AsyncCallback[Unit] .modStateAsync((S, P) => S) : AsyncCallback[Unit] .setStateOptionAsync(Option[S]) : AsyncCallback[Unit] .modStateOptionAsync(S => Option[S]) : AsyncCallback[Unit] .modStateOptionAsync((S, P) => Option[S]): AsyncCallback[Unit]
If you write a callback that must be executed after a state update, you would've learned that
setState(s) >> c
would be a bug because React applies state changes asynchronously. The correct way of writing it would besetState(s, c)
. With this change you could now also writesetStateAsync(s) >> c.toAsyncCallback
. -
Made
Callback(To)
andAsyncCallback
stack-safe -
Added to
AsyncCallback[A]
:def sync: CallbackTo[Either[AsyncCallback[A], A]]
to turn aAsyncCallback
into a synchronousCallback
when possibledef unsafeRunNowSync(): A
which is handy in unit tests where you've taken care to ensure that test code providesAsyncCallback
s that are actually synchronous under the hood. (eg. instead of a real async AJAX call your test provides anAsyncCallback
that returns a test response immediately)def delay[A](a: => A): AsyncCallback[A]
and deprecate.point
- versions of existing methods that
AsyncCallback
arguments, to takeCallbackTo
instances instead:def finallyRunSync [B](runFinally: CallbackTo[B]) : AsyncCallback[A] def flatMapSync [B](f: A => CallbackTo[B]) : AsyncCallback[B] def flatTapSync [B](t: A => CallbackTo[B]) : AsyncCallback[A] def flattenSync [B](implicit ev: A => CallbackTo[B]) : AsyncCallback[B] def handleErrorSync (f: Throwable => CallbackTo[A]) : AsyncCallback[A] def maybeHandleErrorSync (f: PartialFunction[Throwable, CallbackTo[A]]): AsyncCallback[A]
- more convenience methods.
These just delegate to
CallbackTo
but having them on theAsyncCallback
API helps discoverability in addition to convenience:def runNow () : Unit def setInterval (dur: FiniteDuration) : CallbackTo[Callback.SetIntervalResult] def setInterval (dur: java.time.Duration): CallbackTo[Callback.SetIntervalResult] def setIntervalMs(ms: Double) : CallbackTo[Callback.SetIntervalResult] def setTimeout (dur: FiniteDuration) : CallbackTo[Callback.SetTimeoutResult] def setTimeout (dur: java.time.Duration): CallbackTo[Callback.SetTimeoutResult] def setTimeoutMs (ms: Double) : CallbackTo[Callback.SetTimeoutResult]
-
Added
Callback.throwException
and deprecatedCallback.error
so that it's consistent withCallbackTo.throwException
andAsyncCallback.throwException
-
Added
CallbackOption.activeHtmlElement: CallbackOption[html.Element]
which returns the currently focused HTML element (if there is one) -
Added to
StateSnapshot[S]
:/** @return `None` if `value: S` isn't `value: T` as well. */ def narrowOption[T <: S]: Option[StateSnapshot[T]] /** Unsafe because writes may be dropped. * * Eg. if you widen a `StateSnapshot[Banana]` into `StateSnapshot[Food]`, * calling `setState(banana2)` will work but `setState(pasta)` will be silently ignored. */ def unsafeWiden[T >: S]: StateSnapshot[T] def zoomStateOption[T](f: S => Option[T])(g: T => S => S): Option[StateSnapshot[T]] def withReuse.zoomStateOption[T](optional: Reusable[(S => Option[T], T => S => S)]): Option[StateSnapshot[T]] // If using `import MonocleReact._` .zoomStateO [B](o: monocle.Optional[A, B]) : Option[StateSnapshot[B]] .withReuse.zoomStateO[B](o: Reusable[monocle.Optional[A, B]]): Option[StateSnapshot[B]]
-
Added to
StateSnapshot
:(value).readOnly
to create aStateSnapshot
that ignores writes/updates.withReuse(value).readOnly
to create aStateSnapshot
that ignores writes/updates.withReuse.prepareViaProps[P, I]($: GenericComponent.MountedPure[P, _])(f: P => I)
.withReuse.prepareViaCallback[I](cb: CallbackTo[I])
- example of how these can be used -- this is the recommended way to handle state with scalajs-react
-
Changes in
object Reusability
:-
update
.caseClassExcept
to acceptString
arguments instead ofscala.Symbol
s, for which literals have been deprecated in Scala 2.13 -
added the following
def asyncCallbackByRef [A] : Reusability[AsyncCallback[A]] def callbackByRef [A] : Reusability[CallbackTo[A]] def callbackOptionByRef [A] : Reusability[CallbackOption[A]] def callbackKleisliByRef[A, B]: Reusability[CallbackKleisli[A, B]]
-
updated
.deriveDebug
and.caseClassExceptDebug
to add a new option:logNonReuse: Boolean
. When enabled, it will log details to the console when it encounters a non-reusable value.For example, if you used it to derive an instance for
case class Person(id: Int, name: String, age: Int)
and passed inPerson(1, "Bob", 80)
andPerson(3, "Bob", 60)
you would see this in your console:Instance of com.github.example.Person not-reusable for the following reasons: .id values not reusable A: 1 B: 3 .age values not reusable A: 80 B: 60
-
-
Add to
Reusable
:def callbackKleisliByRef[A, B](c: CallbackKleisli[A, B]): Reusable[CallbackKleisli[A, B]]
def asyncCallbackByRef[A](c: AsyncCallback[A]): Reusable[AsyncCallback[A]]
-
Add to
Reusable[A]
:def <*[B](fb: Reusable[B]): Reusable[A]
to combine reusability and return the left valuedef *>[B](fb: Reusable[B]): Reusable[B]
to combine reusability and return the right value
-
You can now (optionally) pass props to and through your Router. See the doc. Thanks @rpiaggio!
-
Testing
ReactTestUtils
methods now only warn when failing to unmount a component, rather than throwing an error- Event simulation in tests now ensures that default properties of events are set.
Example: you don't need to manually specifydefaultPrevented
oraltKey
in aSimulate.click
, they now default tofalse
.
-
MonocleReact
- the
modStateL
andmodStateOptionL
methods now accept all kinds of appropriate optics instead of just lenses
- the
-
Remove API deprecated prior to scalajs-react v1.5.0
-
Upgrade dependencies
- Cats 2.1.1
- Cats Effect 2.1.3
- Monocle (Cats) 2.0.4
- Monocle (Scalaz) 1.6.3
- Scala 2.12.11 / 2.13.2
- Scala.JS 0.6.32 / 1.0.1
- scala-collection-compat 2.1.6
- scalajs-dom 1.0.0
- Scalaz 7.2.30
Avoiding deprecations:
find . -type f -name '*.scala' -exec perl -pi -e '
s/\b(Callback[ \.]+)error\b/\1throwException/g;
s/(?<=[( ,])'"'"'([a-z][A-Za-z0-9_]*)/"\1"/g if /Reusability.*caseClassExcept/;
s/(AsyncCallback\s*(?:\.\s*)?)point\b/\1delay/g;
' {} +
Switching to automatically named components:
find . -type f -name '*.scala' -exec perl -pi -e '
s/(ScalaComponent[ .]+builder.+\]) *\( *".*?" *\)/\1/;
s/(ScalaComponent[ .]+static) *\( *".*?" *\)/\1/;
' {} +