New to Kea or saw it last a few years ago? You should take a closer look.
A lot has changed and you might like what you see! ๐
8 months after the release of Kea 1.0 I'm proud to announce version 2.0!
This version brings several convenience features. It's a rather small release, yet there
were a few breaking changes, which warranted a new major version.
What changed? Read below!
But first! You are reading this blog post in the brand new documentation for Kea! Powered by docusaurus v2!
Over 17000 new words were written for these docs in an effort to really clarify
how Kea works.
Start with What is Kea? if you're new here. Then head on to the core concepts.
Please also read them if you've been using Kea for a while. You might learn something you didn't know!
Then check out additional concepts, debugging and other pages for more.
Anyway, where were we?
Oh yes, new stuff in Kea 2.0! ๐คฉ
Listeners built in (1 Breaking Change)#
For years Kea has supported two different side effect libraries: sagas and
thunks.
With Kea 1.0, I added a new lightweight one called listeners.
Listeners solve the main issue with thunks (you can't use thunks in reducers) and let you
write much simpler code than sagas, while retaining the most commonly used features of sagas
(debouncing and cancelling workers a'la takeLatest
).
Unless you're writing highly interactive applications, you will probably not need to use sagas anymore.
Before 2.0 listeners
was an optional plugin, but now it's included by default.
This enables two big things:
- Much easier to get started with Kea
- Plugin authors have a side-effect library that they can always rely on instead of writing
bindings for 3 different systems.
Weighing at just 1.4KB (gzipped, 3.4KG minified),
including listeners in kea doesn't add a lot of weight.
Yet if you wish to disable them, use skipPlugins
when upgrading:
Breaking change, please note: If you were using listeners with Kea 1.0, make sure to remove listenersPlugin
from
your resetContext({ plugins: [] })
array or Kea will complain that it's being imported twice.
Writing [actions.
and ]
is now optional#
This used to be the only way to write reducers and listeners:
Now you can do this:
If your actions are defined in the same logic (or imported with connect
), you can
skip writing [actions. ]
and also skip ({ actions })
.
Writing [actions.increment]
will still work, just like writing [otherLogic.actions.actionName]
.
This will be especially nice for TypeScript users, who were forced
to write [actions.increment as any]
to avoid constantly bumping into "error TS2464:
A computed property name must be of type 'string', 'number', 'symbol', or 'any'".
Auto-Connect!#
Up to Kea 1.0, when you used actions or values from otherLogic
inside your logic
,
you had to connect
them together.
Now you can skip connect
(if you want to) and call all actions and values directly
on counterLogic
:
While this also works in Kea 1.0 under some conditions, the code above will always work with
Kea 2.0.
In version 1.0 you had to manually assure that counterLogic
was mounted before calling actions
and values
on it. Perhaps it was mounted via useValues
in React or alternatively
you could also write: connect: { logic: [counterLogic] }
without specifying what exactly to
connect. The code above would then also work.
In version 2.0 this is no longer necessary. When you:
- use
counterLogic.actions.increment
as a key in reducers
or listeners
- use
counterLogic.selectors.counter
in selectors
- use
counterLogic.anything.really
inside a listener
... then counterLogic
is automatically connected to logic
and mounted/unmounted when needed.
This means the following code will also work:
In this example, the first time you use counterLogic
is inside a listener when getting
a value
from it.
If counterLogic
was not already mounted, it will be mounted directly when you call showCount
.
It will stay mounted for as long as logic
is still mounted.
It will be unmounted together with logic
in case no other mounted logic or component has a lock on it.
There is one caveat with autoConnect
for when you want to manually call mount()
and unmount()
inside a listener. For that please read the section in the Using without React
page.
To opt out of autoConnect, pass autoConnect: false
to resetContext
.
(Optional) Babel plugin to autogenerate paths#
If you have ever used the redux devtools, to debug your logic,
you will have noticed that unless you specify a path
in your logic, it will be automatically
placed under kea.inline.[N]
like so:
With the new babel-plugin-kea, these paths can be
autogenerated from the filesystem, greatly enhancing your debugging experience:
What's more, this can be used in combination with plugins like
kea-localstorage or in frameworks like next.js to
persist values or hydrate server-rendered logic easier than ever before.
Other smaller improvements#
Those were the big ones. A few other things made it into Kea 2.0.
You can extend reducers#
Previously in this case:
The entire reducer for myValue
would be overridden. This means only the action
doSomethingMore
would have any effect on the value. This is no longer the case and the reducer
mapping is merged when a reducer is extended.
In case of conflicts, later actions override previously defined ones. However the first default
value is taken. To override a default, just specify it separately with defaults: { myValue: 100 }
within kea({})
In resetContext
, createStore
is now true
by default#
Previously when using resetContext
and not using any other redux-specific middleware or libraries,
you had to write:
Omitting this createStore: true
line would cause Kea to fail. This is no longer necessary.
The redux store will be created when you call resetContext
without any arguments.
Pass false
to createStore
if you wish to avoid this.
The path
in your logic can start with anything#
Previously you had to write:
... if you wanted your logic.path
to start with pages
or anything other than kea
or scenes
.
The first part of the path had to be whitelisted.
This is no longer necessary. If you omit paths
in createStore
, you can use whatever string
for the first part of your logic's path.
Specifying paths
reverts to whitelisting and anything else is disallowed.
Only now it will also throw an error instead of silently just not connecting the logic to redux.
Create a reducer without a default#
This used to be the only way to define reducers:
Now if you prefer, you can omit the default value in reducers
:
... and either define it in defaults
or not at all. It'll just be null
if not defined.
Action type string no longer skips scenes.
#
This is a very minor tweak.
Previously if your action had a path
that started with scenes
, then it was skipped in the
action type toString()
.
Now it's included:
I told you this was a very minor tweak!
That's it for new stuff in Kea 2.0. Please let me know what
is your favourite new feature or if you have anything else to share! ๐
What's next? (Kea 2.1 and/or 3.0)#
There are two main things I'd like to explore in the next versions of Kea.
TypeScript support#
One of the most
requested features for Kea has been proper TypeScript support.
While you can get pretty far with Kea in TS if you manually create your interfaces,
this is sub-optimal.
The goal for Kea 2.1 (or 3.0?) is to have full and automatic TypeScript support. In fact,
many of the changes with 2.0 (namely eliminating the need for connect
& no need to write [actions.__]
)
were done to pave the way.
Even if you don't use TypeScript, this will help IDEs offer proper autocomplete support when writing
Kea in regular JavaScript.
Precomplication#
At the end of the day, Kea is just an engine that converts input
into logic
plus a framework
to mount/unmount this logic
when requested by React components.
What if we could do some of this conversion at compile-time, rather than at runtime?
Now that we have a babel plugin that automatically
adds paths to logic, could this be extended to speed up runtime Kea by inlining some of these
conversions where possible? Would it make a difference in runtime speed?
Kea's performance has never been an issue so far, but this is an interesting avenue for some exploration.
To be continued.