A big thanks to my recent patrons who bumped this release up my personal-time priorities and provided significant motivation (through my gratitude) to get through all the work this release needed.
- React 16 support and adherence
- New shapes for
setState
&modState
, and their propagation all over - Type-safe and purely-functional AJAX
- Type-safe and purely-functional refs
- detailed error messages for invalid JS components
(Don't miss the migration section below!)
-
[React 16] New render return types
- Strings
- Numbers
EmptyVdom
Option[_]
VdomArray
ReactFragment
ReactPortal
-
[React 16] keys can no longer be booleans
-
[React 16] Everywhere that there was
setState(S)
andmodState(S => S)
, there is nowsetStateOption(Option[S])
andmodStateOption(S => Option[S])
. -
[React v?] Everywhere that there was
modState(S => S)
, there is nowmodState((S, P) => S)
to use the current component props value when calculating a state change. (I'm not sure in which React release this was introduced; it was either undocumented or I accidentally missed it.) -
[React 16] Add new lifecycle method:
componentDidCatch
. (read about this! It's important. As of React 16 errors bubble up through your component tree and will unmount your entire app if unhandled.) -
[React 16]
.getDOMNode
now returnEither[dom.Text | dom.Element]
instead of justdom.Element
. Implicit methods have been added:.to{Element,Text}
to return anOption[dom.{Element,Text}]
, and.as{Element,Text}
to return andom.{Element,Text}
and throw otherwise. -
[React 16] The
.raw
packages have been migrated to the new types of React 16 and reorganised to match their JS counterparts. Examples:.raw.ReactChildren
=>.raw.React.Children
.raw.ReactComponentEs6
=>.raw.React.Component
.raw.ReactNode
=>.raw.React.Node
- etc
-
VDOM changes:
EmptyVdom
is now aVdomNode
instead of aTagMod
. If you want the old behaviour, useTagMod.empty
- New constructors:
ReactFragment(ns: VdomNode*)
ReactFragment.withKey(key: Key)(ns: VdomNode*)
ReactPortal(child: VdomNode, container: DomContainer)
- New attributes:
hidden
on
- The following attributes are now boolean-only:
async
controls
default
defer
formNoValidate
itemScope
loop
multiple
muted
noValidate
open
reserved
scoped
seamless
selected
-
Added new classes that you can use to pass around a single function that does
setState
ormodState
respectively, with downstream callers able to choose the variation they need. (Eg.setState(s)
orsetState(Option(s), cb)
)SetStateFn[F, S] ModStateFn[F, S] ModStateWithPropsFn[F, P, S] // And aliases: SetStateFnPure[S] SetStateFnImpure[S] ModStateFnPure[S] ModStateFnImpure[S] ModStateWithPropsFnPure[P, S] ModStateWithPropsFnImpure[P, S]
-
StateAccessor
typeclasses now support all the…{set,mod}State…
methods thatStateAccess
provides, including the optionalCallback
args and the new{set,mod}StateOption
methods. This has required a slight change to typeclass usage syntax: instead ofstateAccessor.setState($)(s)
it's nowstateAccessor($).setState(s, [cb])
. -
StateSnapshot
now supports all the…{set,mod}State…
methods thatStateAccess
provides, including the optionalCallback
args and the new{set,mod}StateOption
methods. Most of the DSL to createStateSnapshot
s is unchanged but if you're creatingStateSnapshot
s manually by passing in functions then you'll need to change then fromS => Callback
to(Option[S], Callback) => Callback
orSetStateFn[S]
. (See ReuseExample for an example) -
Add a purely-functional
Callback
-integrated Ajax helper to theextra
module. (demo here) -
Much more detailed error messages when you try to create JS components with invalid arguments. Sample:
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Invalid JsComponent! You've called JsComponent(undefined) Source: com.whatever.finance.money.wealth.MoneyMaker.Component (line #37) Make sure that * your @JSImport / @JSGlobal annotations have the correct values * the JS that you're referencing has been loaded into the JS environment !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
Add
CallbackKleisli[A, B]
(akaKleisli[CallbackTo, A, B]
akaReaderT[A, CallbackTo, B]
, definition:A => CallbackTo[B]
) tocore
module. -
Add to
object Reusable
callbackByRef[A](c: CallbackTo[A]): Reusable[CallbackTo[A]]
callbackOptionByRef[A](c: CallbackOption[A]): Reusable[CallbackOption[A]]
byRefIso[A, B <: AnyRef](a: A)(iso: A => B): Reusable[A]
- Compare by reference through an isomorphism
-
Added
Reusability#logNonReusable
to log a warning when instance is non-reusable -
Add
Px.ManualCollection
which is a little add-only mutable collection of manualPx
thunks, with a refresh function that refreshes everything in the collection at once. Idea is that you create and store it in your component backend, registerPx
s as you create them, then callrefresh()
in your render function. -
Added type class instances:
cats.arrow.Arrow[CallbackKleisli]
cats.arrow.Choice[CallbackKleisli]
cats.MonadError[CallbackKleisli[A, ?], Throwable]
cats.MonadError[CallbackTo, Throwable]
scalaz.Arrow[CallbackKleisli]
scalaz.BindRec[CallbackKleisli[A, ?]]
scalaz.Choice[CallbackKleisli]
scalaz.Distributive[CallbackKleisli[A, ?]]
scalaz.MonadError[CallbackKleisli[A, ?], Throwable]
scalaz.MonadError[CallbackTo, Throwable]
scalaz.MonadPlus[CallbackOption]
scalaz.MonadReader[CallbackKleisli[A, ?], A]
-
Added
ext-monocle-cats
module with the same API asext-monocle
but using the Cats version of Monocle. -
Removed
CallbackOption#get
- deprecated in 1.0.1CallbackOption#toBoolCB
- deprecated in 1.0.1.isMounted()
- removed by ReactReactAddons
- No longer has any contentCSSTransitionGroup
- React have moved this into its own separate libraryPerf
- React have removed this entirely in React 16
ScalaComponent.Builder#buildWithReactCreateClass
-React.createClass
was removed in React 16WebpackRequire
- deprecated in 1.0.1
-
Upgrades
- Cats 1.0.1
- Monocle 1.5.0
- Scala 2.11.12
- Scala.JS 0.6.22
- Scala.JS DOM 0.9.4
- Scalaz 7.2.20
-
EmptyVdom
is now aVdomNode
instead of aTagMod
. If an emptyTagMod
is actually what you want useTagMod.empty
instead. -
Refs have are now pure, type-safe, and NPE safe. Migrate with:
find . -name '*.scala' -type f -exec perl -pi -e 's/((Js|Scala)Component)[ .]+mutableRefTo/Ref.to$1/g' {} + find . -name '*.scala' -type f -exec perl -pi -e 's/(?<=[ .])ref\( *([a-zA-Z0-9_]+) += +_ *\)/withRef($1)/g' {} + find . -name '*.scala' -type f -exec perl -pi -e 's/^( *(?:private\S*|protected)? +)var +([a-zA-Z0-9_]+) *: *(((html|svg|dom)\.|(HTML|SVG))[a-zA-Z0-9_.]+) += +(?:_|null) *$/$1val $2 = Ref[$3]/' {} +
Usage changes are as follows:
class Backend($: BackendScope[Unit, String]) { - var inputRef: html.Input = _ + val inputRef = Ref[html.Input] def handleChange(e: ReactEventFromInput) = $.setState(e.target.value) def clearAndFocusInput() = - $.setState("", Callback(inputRef.focus())) + $.setState("", inputRef.foreach(_.focus())) def render(state: String) = <.div( <.div( ^.onClick --> clearAndFocusInput, "Click to Focus and Reset"), <.input( ^.value := state, ^.onChange ==> handleChange) - .ref(inputRef = _) + .withRef(inputRef) ) }
val scalaRef = - ScalaComponent.mutableRefTo(MyScalaComponent) + Ref.toScalaComponent(MyScalaComponent) val jsRef = - JsComponent.mutableRefTo(MyJsComponent) + Ref.toJsComponent(MyJsComponent)
In your unit tests you often just want to get the ref value without worrying about safety; for that case, call
.unsafeGet()
on your ref. -
.getDOMNode
used to return adom.Element
but now returns anEither[dom.Text, dom.Element]
because that's what React 16 now returns..getDOMNode.map(_.asElement)
and.getDOMNode.asElement
are replacements for.getDOMNode
in the pure/CallbackTo
and impure/Id
flavours respectively.It's usually the pure version in main code and the impure one in unit tests so use the following to migration commands to help you along:
# .getDOMNode => .getDOMNode.map(.asElement) find . -name '*.scala' -type f -exec perl -pi -e 's/(?<![a-zA-Z0-9_])getDOMNode(?![a-zA-Z0-9_])(?![. ]dom(?:as|To)Html)(?![. ]domCast)/getDOMNode.map(.asElement)/g' {} + # .getDOMNode => .getDOMNode.asElement find . -name '*.scala' -type f -exec perl -pi -e 's/(?<![a-zA-Z0-9_])getDOMNode(?![a-zA-Z0-9_])(?![. ]dom(?:as|To)Html)(?![. ]domCast)/getDOMNode.asElement/g' {} +
-
If you use Scala Test-State, you'll need to upgrade to 2.1.3.
If you like what I do —my OSS libraries, my contributions to other OSS libs, my programming blog— and you'd like to support me, more content, more lib maintenance, please become a patron! I do all my OSS work unpaid so showing your support will make a big difference.