Loading video player...
Hey friends, welcome to the Swelt 5
course. This course was made for new and
existing Swelt developers. In this
course, you're going to get hands-on
experience starting from the basics to
more advanced topics. You're not only
going to learn how to use Swelt, but
also how it works. I believe that
knowing how things work gives you
greater joy by being more competent at
what you do. This course was a lot of
work. So before we get started, I would
appreciate it if you subscribed and
engaged with the video by liking and
leaving a comment for the algorithm and
listening to the message from our
sponsor. This video is sponsored by
Sentry. If you want to see all of the
errors in your application and have more
insight when things go wrong instead of
guessing, you want to check out Sentry
at sentry.io. The worst case scenario is
when something goes wrong in your app
and you don't know about it, costing you
a potential customer. Sentry gives you
complete visibility in case something
goes wrong with error monitoring to get
actionable insights to resolve the most
important code related issues and other
features like log so you can see what
happened and why. Session replace to get
to the root cause of an issue faster by
watching actual replays of real user
sessions. Tracing to find out why an API
is slow or where a crash came from. And
Sentry even has a new AI debugging
agent, Sier, that can debug the root
cause of an issue and open a pull
request with a fix for you. Sentry
supports almost everything and makes it
incredibly easy to get started with SW.
If you're using Swellit, you can use
their installation wizard to set
everything up for you or you can do a
manual setup. Thanks to Sentry, Sulki
now has even first class support for
observability with built-in open
telemetry tracing and a dedicated
instrumentation setup file that ensures
your monitoring tools work seamlessly.
If you're a solo developer, you can get
started today for free with Sentry's
generous free tier. So, make sure your
user experience remains great by using
Sentry at sentry.io. Thank you, Sentry,
for sponsoring the video. All right,
before we get started, you're going to
need Node.js JS to run and install npm
packages. So you can go to the node
website and under get NodeJS, you can
pick your target platform. For the code
editor, I'm going to use Visual Studio
Code, but any VIP coding fork of VS Code
is going to work. And of course, we're
going to need the swelt for VS Code
extension for syntax highlighting and
other features. If you're using another
editor like Jet Brains or Vim, you can
find the Swelt language server for your
editor.
Usually to set up swelt you would use
the vit C cli by saying npm create vit
at latest and then you would get a bunch
of options for other frameworks but you
could pick swelt and you can pick if you
want typescript or javascript in this
course I want to focus on swelt and I
think that types are just visual noise
so I'm not going to be using typescript
but if you care about types I'm going to
include a link to a post where all of
the examples are going to be typed and
this course is also not going to be
focused on swell kit the meta framework
for swell If you want something with
more opinions that has routing,
serverside rendering and so on, then
once you're done with the course, I
encourage you to look at swell kit. In
this case, we're not going to use the
vit CLI, but a swel starter that I
prepared. The swell starter includes the
assets, styles, and packages that you're
going to need through the course. It
also uses the minimal CSS framework,
Picos CSS, so everything looks great out
of the box. This way, we can focus on
what's really important. To get started
with the SWEL template in your terminal,
you can type npx dit the name of the
repo which is going to be joys of code
/arn-swelt.
And you can name the folder whatever you
want, but I'm going to name it
learn-swelt.
This is just going to copy the repo
without a commit history and it should
take a second. All right, let's cd into
learn swelt. All right, so now that
we're inside learnswelt, we have to
install the dependencies. So we can say
npmi and this should take a second. Once
it's done, you can run the development
server by saying npm rundev. All right,
let's go to the browser. Now you can
open a new tab and let's navigate to
localhost 5173 which should open the
default example. Now you should have
swelt running on your machine. All
right. So what is swelt? If we go to the
swelt web page, we can see that swelt is
a word that means attractively thin,
graceful, and stylish. And here we have
the definition. Swelt is a UI framework
that uses a compiler to let you write
breathtakingly concise components that
do minimal work in the browser using
languages you already know, HTML, CSS,
and JavaScript. So, let's actually
investigate this claim before you even
start learning about swelt. So, here I
am inside of VS Code and I already
opened the learn Swelt project and this
is just a minimal VI project so we keep
everything simple. And if you aren't
familiar with Vit, here we have index
html which is the entry point of our
application. Here we have the public
folder so you can put your assets inside
of here and the most important source
folder where we're going to keep our
code. So let's open the app.swel file.
Let me bump the font size up. As you can
see here, we have something that looks
like regular JavaScript and HTML. But
unlike regular JavaScript, this count
value here is reactive. So if we go to
our example here, and we can just
increment the count and you're going to
see it's going to be reactive. So the
advantage of cell being a compiler is
that you can take the syntax of
JavaScript and change the semantics for
a better developer experience. In this
example where we have count++ here we're
actually using reassignment to update
the value reactively. So you can also
say plus= 1. And this is the same thing
of course and we can actually see the
compiled output. If you have the spelt
extension you can press control shift p
and you can type show compiled code here
and it's going to show you the compiled
code. As you can see, this is human
readable JavaScript. Of course, I
cleaned it up a bit so we can actually
examine it more. So, let me just open
this empty tab and I'm going to copy
paste some code here. So, here we have
this function app which accepts a
target. And then we're creating this
reactive value which is actually a
signal under the hood. And then here
we're creating the DOM elements which is
the button in this case. And then we're
going to append it to the DOM. And here
we're just retrieving the text node from
the button. And then this is the
reactive part. So we can update the DOM
when state changes using this template
effect. And later we're going to learn
more how this works. But as you can see,
what we learned from this exercise is
that swell doesn't have a virtual DOM.
It doesn't rerender the entire component
when your state updates like React. It
does granular updates to the DOM when
the state changes. This is what makes
SWEL fast and performant and what it
means to do minimal work in the browser.
All right, let's start from the
beginning. Every file that ends with the
dotselt extension is considered a swelt
component. To be more precise, it's a
single file component. As the name
implies, a single file component means
that all of your logic, markup, and
styles are inside of a single component.
So, for example, here we can create this
logic block. We have markup
and styles. So, let's create a script
tag here. And then we create a variable
named title that has the value of inside
of the markup. You can put anything you
want. You don't need a special template
tag or anything like this. You can just
create elements directly. So let's
create a heading and then we can use an
expression inside of it. So we can use
the opening and closing curly boys. Now
we can press save and we can see swelt
in the output. But of course we can add
some styles. So let me create a style
block. We're going to target the h1
element. Let's say color orange red.
Same as swelt. And that's basically it.
I'm going to save it. And now we're
going to see we have this orange text as
well. And of course, the order of the
blocks isn't important. For example,
even if I take this style block and I
put it at the top, I'm using prettier.
So when I'm going to save it, it's going
to reorder it for me. So I'm pressing
save. And as you can see, it reordered
the blocks. So the order of the blocks
isn't important at all. Of course, you
can also use TypeScript. You can specify
here leng. And then you can say
TypeScript here. And of course, this is
inferred, but we can use types here. We
can say string. And you can even use
TypeScript because it's natively
supported in the template. So we can say
title as string. But of course, I'm not
going to focus on TypeScript in this
course because I actually think it's
very distracting when you're learning.
It's just visual noise. But if you care
about types, I'm going to link to an
accompanying post in the description.
All right. So lowerase tags behave
exactly like regular HTML elements, and
so do attributes. So let's for example
create an image here. We can pass it a
source attribute.
Let's say image GIF. And we're going to
see something also very cool. So when we
do this, we're going to get a warning
from cell that says image element should
have an alt attribute. So let's do that.
Let's add a description. We're going to
say alt
person nodes. Let's save this and we're
going to see the output. How wonderful
is this friends? All right. So going
back, we also have the power of
JavaScript. So we can create the script
tag. Let's create a variable source.
We're going to say image.give.
And let's also create another one. Name
it alt. And we can also give it a
description.
Awesome. So now we can use an expression
here. We can say source.
And we can say alt. Another cool thing
is if the attribute name and the value
have the same name, you can just use the
shortand. So in this case, instead of
source equals the source expression, we
can just say source. But let me actually
show you something cool. Because I'm
using prettier, it's automatically going
to do that for me. All right. So, I'm
going to save right now. And you're
going to see it works the same as
before. But of course, we can also use
an object. So, I can say let object,
let's say source
image.g. And then let's give it a
description. And we can say person not.
So now we no longer need this. And of
course, this would be really tedious if
you had to do something like this.
source equals object source alt equals
object alt. Of course, it works as
expected if you look here, but there's a
simple way to do this and we can spread
this object on this element. So, we can
just remove all of this, use the curly
boys and we can just use the spread
syntax. So, we can spread this object
and everything works as expected.
There is one more thing regarding
attributes that I want to show you and
that is how to conditionally render
them. So, often people make this
mistake. they define some piece of state
like lazy let's say false and then maybe
you want to conditionally render this
attribute loading here so we can pass an
expression and then you can say okay
lazy in that case we can use short
circuit evaluation and then we can say
lazy all right anyway we think since
this is false this isn't going to be
rendered but that is wrong and also
typescript gives you a warning here
because the types don't match so here
for example if I save and then I go here
and inspect the element you're going to
see we're going to get loading equals
false which might be completely
unexpected Or you might do something
like this. You might use a turnary. So
let's say lazy. And then in that case,
we can actually show lazy. Otherwise,
you might be thinking to yourself, okay,
let's just use an empty string here. And
again, we get this warning in our
editor, which is super useful. In this
case, when I save this, we're going to
see we're going to get an orphan
attribute loading. All right. So the
solution to this is to never use short
circuit evaluation like this or this
turnary where you return an empty
string. Always use null or undefined. So
for example, if I say no here, you're
going to see TypeScript is going to be
happy. So now if I save this, you're
going to see this behaves exactly as you
would expect and the attribute isn't
rendered.
All right, so let's talk about component
styles. Let's create a script block with
a color variable. So let's say orange,
red. Let's create a heading with some
text inside. And I heard a bunch of you
like some inline styles with this thing
called tailing CSS. So, of course, you
can just use the style attribute and you
can include all of your styles inside of
here, right? I'm just joking. But the
style attribute is really cool in SW and
I'm going to show you why. So, we can
just use a regular CSS property. And
here we can use an expression. So, you
can pass color and you're going to see
it works as expected. But there is also
a shortand you can use. So, instead of
saying style equals, we can just use the
CSS property using colon color. Then we
can remove this and we can just leave
the expression in like this. Boom. And
since the variable name is the same name
as the CSS property, we can also omit
this part. So you can remove this. And
I'm going to save this. And you're going
to see it works as expected. The only
thing you can do with a style attribute
like in another framework, you can't
really pass an object. So something like
this isn't going to work. If you say
style equals, and then you may want to
pass a color. That being said, I don't
think I actually ever needed this use
case. And if you want something like
this yourself, you can write a simple
utility function. The style attribute is
also great for using CSS variables. Let
me show you how cool that is. So here we
can create a style block. Let's just say
H1. And then we're going to define the
color. Let's use a CSS variable. By
default, we're going to pass color. If
that doesn't exist, we can specify a
default value as white. And here we can
just use a CSS variable
dash color.
And we can just use our color as before.
And of course we can also use the
shortand. So here we can say d-color.
And then you can say equals. And we can
pass in the expression or a color. All
right. So if I save this, you're going
to see everything is going to work the
same as before. And this is something
that I absolutely love about swelt.
Thankfully, you're not just limited to
the style attribute. So let me just
remove all of this code.
And we're going to say that color should
be orange red.
And this leads us to another awesome
thing about SWEL. By default, any styles
that you define inside of a SWEL
component are going to be scoped to that
component. That means that the color we
define here is only going to affect the
heading inside this component and not
globally. So if we go here to our
example and open the developer tools, we
can go to the head and see what swell
generated. So we can see that swell
generated a unique class name. So this
way your styles aren't going to clash
with anything else. You can also of
course import CSS inside of your
component. So for example, if you have a
script tag here, you can say import and
then you can say something like app.css
and then you're going to import styles
that apply to this component. But of
course you can also have global styles.
So if I remove this, I'm going to go to
the root of my app which is main.js.
And here I can import app.t CSS which
I'm already using. So let's actually
look at app.tss. So here I have just
some basic imports and some CSS
variables. So if you want to define
something global here, you can just go
here and you can say for example H1
color red and now we have global styles.
That's how easy peasy lemon squeezy that
is. But of course I'm not going to do
that. So now you know how to apply
global styles if you have to. So
speaking of global styles, you can also
have global styles in your component. So
let's say for example that you receive a
post from a content management system
and maybe you don't have control over
it. So here I have some piece of content
and let's say I want to render it. I can
use H group and then we can use the
special at HTML tag in SW to render raw
HTML. So we can say HTML content. Let's
save it. And you're going to see it
works as expected. So let's add some
styles. So we can say style. Let's
target the H1. Let's say text transform.
And we can say capitalize. What you're
going to see, we immediately get a
warning in the editor by SWelt undue CSS
selector H1. And that is because in
quotes SWelt can't see that this style
is being applied here. So, it's going to
be removed.
And for this reason, we can use the
global style modifier. So, we can say
colon global. Let's use parenthesis
like this. Save. And you're going to see
everything works as expected. All right.
But this would be really tedious if you
had to do this for every element. So for
example, if you had to do global P and
etc. Now it's really annoying, right? So
let's say we have some styles here,
whatever. And we can actually get around
this by just using global. So here's
what we can do. We can say global. Then
we can just use the curly boys. And then
we can just move the styles inside of
here.
And of course, we don't have to use
global.
Let's just do this. Remove it
like here. And now we're going to see if
we save this everything is going to work
as expected. And this is only
complaining because we don't have any
styles here. But we can for example say
color orange red.
And now we're going to see it works as
expected. Of course, one downside of
this approach is that these styles can
clash with your global styles. But we
can also fix this. You can have global
scope styles. So what do I mean by that?
So here we have this HG groupoup
element. before global we can actually
just say H group and it's going to be
scoped just to this element. So let's
save this. All right. So we can open the
developer tools to see what SW
generated. Let's go to the head. We can
open the style block and we can actually
see here SW generated this unique class
name but it's prefixed with H group. So
this way you can have globally scoped
styles. You can also have global key
frames. So, for example, inside of here,
if you use a key frame, it won't really
work. But you can do this. You can say
add key frames.
And now you can just preface this with
global. And you can just use the name of
your animation. You can say animation.
And then you can define your animation
inside of here if you want to make it
global for whatever reason. And there is
one more cool thing about the style
block and that is that you can use a
different pre-processor. So similar to
TypeScript, you can define a lang
attribute here and you can define SCSS
for SAS or you can use post CSS and it's
going to work out of the box. How cool
is that? All right, so let me show you
another cool feature of SWEL which are
dynamic classes. So for example, let's
say we have an accordion here. We can
say open false and then we can create
button with a class button and then
we're going to have a span item A and
then another span. So we can say point
let's see use this emoji. So this is
going to be our trigger
and then of course we can use an
expression inside of here. All right if
open is true then we can say apply an
open class otherwise do nothing and then
we can create some styles. So we can say
trigger. We have to say that this is
display inline block. So our animation
works
and let's just say transition. We're
going to use rotate. So we can say
rotate 2 seconds
ease
and then we can say hey if this has an
open class then we're going to animate
it. So let's say rotate
minus 90°.
All right but in this case this isn't
reactive yet. We're going to learn about
reactivity in the next section. But for
right now we can just say state false.
And then here we can just say on click.
And then we can just set open to the
opposite.
All right. So let's save this. And we
should see, let me just zoom in. We
should see this works as expected.
Awesome. All right. But applying classes
like this can be really tedious. So
there's actually a better way. We can
use the class directive to conditionally
apply classes. So we can remove all of
this and we can say class colon and then
we can say open. And of course you can
base this on some other expression. But
in this case we can just remove this.
And now I can save this. And you're
going to see it works the same as
before.
You can also pass an object or array to
the class attribute. So let me show you
how cool this is. So we can say class.
And let's first start with an object. So
we can say trigger. And if this is true
then this is going to apply this class.
We can also just pass open. So now this
is going to be trigger and open
conditionally. All right. Let's save
this and it should work the same as
before. Of course, you can also pass an
array. So, for example, if I pass an
array here, we can first define our base
styles. So, it's going to be trigger and
then we can apply open conditionally.
So, we can say if open is true, then
this is going to be open. Let's save
this. And you're going to see it works
the same as before. Or instead of this,
you can pass an object, which is a lot
simpler in my opinion. So, you can just
say open
and it works the same as before. And
under the hood, this uses the CLSX
library to merge all of these styles
together. And this is really useful if
you're working with Tailwind and you
need to apply a bunch of classes. So for
example, here again we have our base
styles, we can say transition transform.
And if you want to apply some tailwind
class like for example rotate 90, we can
just make it conditional like this and
it's going to work beautifully. All
right, but there's something else that
you should consider. So instead of
applying a bunch of classes, if you have
something more complicated, use data
attributes instead. So for example,
instead of this being a boolean, we can
say status and this can be open or
closed. So in this case, this on click
really doesn't matter. Let me just
remove this and then we can just go
here. We can say class trigger. So we
are separating our styles for styles and
our logic for data attributes, which is
really cool. So we can say data status
and then we can just say status. And now
if you have something more complicated,
now it's way easier to orchestrate your
transitions. So instead of using a bunch
of classes, we can just go here and we
can say data
status
and we can say open. And now if you have
more of these styles, of course, now
it's way easier. You can do whatever you
want. It's way more explicit.
All right, let's talk about soil
reactivity. In the context of JavaScript
frameworks, application state refers to
values that are essential to your
application working and cause the
framework to update the UI when changed.
All right, so let's look at a counter
example. I'm going to create a script
tag. Let's say count equals zero. Then
I'm going to create a button. We can use
an expression inside. Let's add an
onclick event handler.
And we can say count equals count + one.
But of course this isn't reactive. And
we even get a warning from swelt. Count
is updated but is not declared with
state. And we can see it ourselves that
count is not going to reactively update.
To create a reactive piece of state in
swelt, we use the dollar sign state
rune. So we can see it looks like this.
State we can say zero. Now it no longer
complains. If you look at our example,
we can increment the count. And this
might look like a function, but it's
just part of the language. So, Selt is
actually going to turn this rune into a
signal under the hood. And because this
looks like a regular function, if you're
using something like TypeScript, you can
just pass your type in here and it's
going to work.
As you can see here, we reactively
update this value by using assignments.
So, we can reassign count by saying
count + one. Of course, you can be much
simpler about this. So you can say count
plus equals 1 or you can just say count
++. We can save this and we're going to
see that it's going to be reactive.
We can also have deeply reactive state.
And what do we mean by that? Well, let's
say for example that you're working on
something like an editor. So we can say
editor state. And now if you pass an
array or object inside of editor, it's
going to become a deeply reactive state
proxy. So what does that mean? But that
means if you for example change
something like editor content, it's
going to reactively update. Same is true
if the editor is an array. So we can say
editor push one and it's going to
reactively update. And that is really
cool. So here we can create a theme.
We can say dark by default. Let's add
some content.
And now we can render the content. So
you can use curly boys HTML editor
content and this works as you might
expect.
All right, but let's actually change the
content by using a text area. I'm going
to remove this.
Let's say class is going to be editor.
And now we can say value equals
editor content.
And now we want to update. So we can use
on input. we get access to the event and
now we can say editor content equals e
target value and we can also disable
spell check.
All right, so let's save this and now we
can see it works. Okay, but something
like an editor might become quickly
complex and maybe you don't want to
reactively update the entire editor
object when a property changes. In that
case, you can use the state raw rune.
And now when you save it, you're going
to see it's not going to reactively
update. The only time it's going to
reactively update is when you reassign
the entire object or array if you're
using that. So for example, here we can
take this.
Let's just like this. So this isn't
going to work.
It's only going to work if you reassign
the object. So we can say reassignment.
And then let's say editor equals.
We can spread all of the old values and
we can say content e target value. Let's
save this and we're going to see now
it's going to work. All right, but I
want to show you another useful state
room. So let's actually undo this and
make it just state. So we can log editor
and we can see that this is a proxy. So
if we open the developer tools, we can
see the proxy object is here. And this
is a bit awkward because we have this
target and we have to dig our way
through this content. But this might be
even worse if you pass this to some API
that doesn't expect a proxy. So let's
say for example that you want to save
the state of the editor
and now we can define const editor state
and maybe we want to clone the object.
So we can use structure clone and we can
pass the editor. But now if we try to
save the editor so you can say save
editor state.
So now we get this error that it failed
to execute the structure clone because
it expects a regular object and we
passed a proxy. To fix this problem, we
can use the state.napshot rule. So we
just have to go here. We can just say
state snapshot and then we can pass our
editor. And this is going to give us the
actual value. So we can even console log
editor state. Let's save it. Now if you
refresh it, we're going to see that we
just get a regular object. And what's
also interesting is that swell does this
under the hood. So let me just delete
this and previously we said console log
editor.
Now you can see that actually swell
detected that you're using console log.
So it actually did state.napshot for
you. So you get the actual value and
that's really cool.
And the last thing I want to show you is
that you can actually destructure the
value from this deeply reactive proxy.
So we can go here and we can use curly
boys. We can pluck theme and content.
Yoink. Now we can remove all the
references with editor dot.
Great. We can remove this. And of course
we can delete this code. And this works
right now. All right. So we can save and
we should see that this is reactive.
Awesome. But it's not going to work if
you try to dstructure these values where
they weren't defined. So for example,
let's keep this. But we're going to name
it editor. And if you for example go
here and say let and we structure these
values from editor, this isn't going to
work.
But if you want to do this, you can use
the derived rune which we're going to
learn about next. We're going to see is
going to be reactive.
All right, let's talk about derived
state. So I'm going to create this count
here. Let's initialize it as zero. We're
going to have a multiplication factor.
And then let's also create the result
which is going to be the outcome of
count multiplied by the factor.
And we can already see that swell gives
us a warning. This reference only
captures the initial value of count.
Reference it inside a derived instead.
And this is where the derived rune comes
in. So this has different names
depending on the framework you're using.
Some frameworks call this computed
values. Some frameworks call this
derived values. In case of state to
derive the value we can just use the
derived rule and that's it. Now each
time count or factor changes result is
going to update. So let's actually do
that. Let's create a container.
Now inside of here we can say count
times factor
is going to be equal to the result. And
let's also create the buttons so we can
change the values. So we can say count.
Let's add an event handler.
We can say count plus+. We can do the
same for the factor. Say factor.
All right. And now we can save this. So
now we can go to the example and we can
see that everything works as expected.
And that's how simple it is to create
derived values.
There's one important thing to know
about derives and that they're lazy
evaluated. And what do I mean by that?
Well, let's actually just first delete
all of this code. So we can just keep
count here.
And that's it. So let's say you have
some value max which is a derived. So we
can say derived and we can say count is
greater than or equal to four. And we
can go here and add a disabled
attribute. For example,
what's really important to know is that
the right values only run when they're
read like in this example where we pass
max to disabled and they only update
when they change and not when their
dependency change to avoid unnecessary
work. So in this example here where we
have this derived max value even though
it depends on count max isn't going to
update when count changes is going to
update if it changes due to count
changing. So we can actually see this by
using the inspect rule which is a really
useful way to log when a reactive value
changes. So you can say inspect max and
this is only going to log when max
changes. So when we go 1 2 3 4, we
should not see max being logged. So
let's save this. Let's go to our
example, open the developer tools,
you're going to see first we get init
false and we're going to see 1 2 3 4.
Nothing happens until we reach the
number four. Only then is the derived
value going to update.
What's actually cool about signals is
that they only have to be read to become
a dependency. So let me show you what
this means. I'm going to delete this and
let's simplify this expression. Under
the hood, actually turns this derive
into a function. And count is actually
something like this. Count get greater
than four. But the cool part is that we
can just create a function with some
state inside of it and it's going to be
reactive. So let me actually show you.
Let's create a function limit and then
let's say we're passing count and we can
say return count greater than four. But
this actually doesn't work how you might
expect. So for example, if we say here
limit and we pass count, count is going
to be get count. So this function is
going to be tracked inside of this
derived. This is what's actually going
to rerun when count updates and max
updates. Count is not some magic
reactive container. And I can prove this
to you by showing you that passing count
is completely optional. So we can
actually remove count.
And now inside of our function I can
just take this remove count. And now
this is going to be get count. As long
as the signal is red is going to become
a dependency. So this limit function is
going to rerun when max changes. If you
just pass count to limit here, it
doesn't mean anything because it's just
a regular value. The only thing that
matters is that count is red. So if you
want to be explicit, you can pass it as
an argument, but you don't have to.
All right, but what if you have
something more complex? So for example,
let's create a card here. We're going to
say state, and we're going to pass an
array of items. So we can say item,
and let's pick a juicy apple. And let's
say the total is 10.
Now let's pick a banana.
Now let's say that we want to derive the
total sum. We can use the derived rune.
So we can say total derived. But how can
we actually do this? We can only pass an
expression inside of here. You could
actually use an array method like reduce
or map. But let's say that you just want
to loop over the items and return a sum.
In that case, we can use derived by and
we can just pass a function inside of
here.
And now we can say let sum equals zero.
And now we can loop over the items and
get the sum
and let's return the sum.
That's it. So now in the template we can
create a paragraph tag. We can say total
and as you can see it works as expected.
The only thing you shouldn't do is use
the drives for side effects and swelt
isn't even going to let you change state
inside of them to save you from
yourself. So for example, if I want to
push something to the cart here. So let
me actually just copy over this apple
and let's see what happens. And now we
should get an error in our console. It
says updating state inside arrived or a
template expression is forbidden and
swelt even tells you that if the value
should not be reactive you should
declare it without state but I think
some use cases are fine like for example
let's say that you want to fetch some
data inside of derived you're actually
not doing any side effects so if you
wanted you could do something like that
since everything is contained inside of
that function
all right so to finish the holy trinity
of reactivity in swelt let's talk about
the effect rune eventually you're going
to have to do some side effects like
fetching data from an API or
manipulating the DOM directly. In that
case, you want to use an effect.
So, let's create account here
and then we can create a button.
We can say count++. All right. So, to
create an effect, we just use the dollar
sign effect rune.
And really effects are just functions
that run when their dependencies change
or when the component is removed from
the DOM. Any state that is read inside
of an effect is going to be tracked. So
we can say console log count and now
count is going to be tracked. As you can
see there is no dependency array.
Now we can open the developer tools and
when we increment count we're going to
see the value log to the console.
One important thing to keep in mind is
because how of signals work values are
only tracked when they're read. What
does this mean? Well, let's say for
example that we have a condition here
and let's say this is false. So we can
copy over this button
and we can say toggle.
So now we can say condition equals the
opposite of condition. And let's also
add a container here.
So now inside of the effect if we use a
conditional block like an if. So if we
say if condition
and then we log the value of count in
this case since condition is false only
condition is going to be tracked since
it's red and not the count.
So going back to the example if we
increment the value of count
you're going to see it's not going to be
logged as expected but also your effect
isn't going to rerun. But of course, if
we change the condition from false to
true, then it's going to log.
In this example, if condition is false,
then the only value that's going to be
tracked is condition. And if condition
is true, then both condition and count
are going to be read and tracked inside
of the effect.
That being said, there's an exception to
the rule what gets tracked. If a value
is read a synchronously, like inside a
promise or a timer inside of an effect,
it's not going to be tracked. So, let's
look at an interval example.
Let's say count the same as before.
And let's copy this over. This is going
to be our delay.
And this is going to be 1 second or
1,000 milliseconds. So now in the
template, let's create a div. We can say
count. And let's create the buttons to
control the interval speed.
So this is going to slow down the
interval. So let's say on click.
So we can say delay times equals 2. All
right. So let's copy the button.
So to make it faster, we can just divide
it by two. And let's add a container.
Great. Now let's create an effect.
Let's create the interval.
So now we can pass a call back to the
interval and we can say count plus+. So
we want count to be incremented every
second. So you can also pass a delay. So
in this case because we're reading this
value in an asynchronous function like
set interval. It's not going to be
tracked. But what is going to be tracked
is going to be the delay. So let's go to
our example. And you can see it works as
expected. Well kind of. Let's try to
make the interval faster.
And that works. Okay. So, let's make it
slower.
Uh, but that just makes it go faster.
And of course, that is because this
effect runs each time delay updates and
it creates new intervals. Thankfully, we
can return a cleanup function from the
effect. So, we can say return
and we can say clear interval and we can
pass our interval and this function is
going to run each time its dependencies
change or when the component is removed
from the DOM. So now we can go back to
our example. We can see it works as
expected. So let's speed it up.
All right. So let's see if we can slow
it down. As you can see, it works as
expected.
If you don't want to track a value
inside of an effect, then you can use
the unttrack function from SW. So let's
look at an example. So let's create two
pieces of state A and B.
Let's copy it over.
Then let's create the button to
increment them.
And we can copy this over. Let's say B.
All right. So now we can create the
effect.
Let's say that we want to log the sum of
A and B.
Also, let me add a container here.
All right. So now if you look at the
example, if we increment the value of
count is going to log the sum.
And if we increment the value of B, it's
also going to log the sum.
But what if we don't want to track A? We
only want to log the sum when B updates.
In that case, we can use the unttrack
function from swelt.
Let's start typing a track. And if you
press enter, it should autoimp import
for you. The unttrack function accepts a
call back. So we can just pass it a call
back. And then we can pass the piece of
state that we don't want to track. And
that's it. Now a isn't going to be
tracked. So now if you go back to our
example, if we increment the value of
count, of course, it's going to update
in the background, but it's not going to
log the sum.
But if we increment B, then it's going
to log the sum.
All right, let's talk more about
dependency tracking inside of an FX. So
let's say for example that you have an
object and array. So we can create an
object state.
Let's pass current and then let's say we
have an array
and this is just going to be an empty
array. So now we're going to have a
button so we can update these values.
So inside of here, we're just going to
say object current++
and array push one. All right. So let's
create an effect.
Now if we say console log object and
let's also copy this effect.
So we can say array. You're probably
expecting that this is now going to
track the object and array, but that's
really not the case. So, we can see if
we update these values, nothing is going
to happen. And that is because the
effect is only going to rerun when the
object changes and not its properties.
So, we have to be more explicit. We have
to say that we want to track object
current. And in the case of arrays, this
isn't going to be enough. We have to
track array length. Now, the FX should
run.
As you can see, it works as expected.
But of course, we can get around this.
Let's say, for example, that we want to
store this object value inside of local
storage.
And we say JSON stringify
object, then we actually don't have to
track object.curren for it to be
tracked. The entire object is going to
be tracked. And this is a pretty cool
hack if you want to react to this object
changing. So for example, you don't even
have to use JSON stringify directly. You
can just use it to track this dependency
and then you can do something else when
this object deeply changes. But of
course, if you only want to track
reactive values updating, then you
should use the dollar sign inspect rune.
So now we can just say object array and
it should work. And you can also
customize what happens when you inspect
a value. So you can say width and then
you get the values. But of course you
can also do other things like run
console trace and etc. You can do inside
of here whatever you want.
In general, effects are something that
you're rarely going to use, but you
should especially avoid using effects to
synchronize state. So, let's look at an
example.
So, let's create count
and then let's create double.
Next, we're going to create a button.
Let's add one click.
And then we can increment count.
All right. So let's create an effect.
So let's update double when count
changes.
Unless you have to, you should
absolutely avoid using effects to
synchronize state like this because you
might run into unexpected results like
your state being out of sync. So let me
actually show you this in practice here
where we increment count. Let's say that
we depend on double updating.
So we can console log
count and double. And now in our example
when we increment count you're going to
see the value of double is going to be
out of sync.
And that is because effects run last. To
be more precise, Swelt cues your effects
and runs them on the next microtask
before the DOM updates. So, we can
actually test this claim. So, let's go
here and we can say console log. We're
going to say script. Let's copy the
console log.
Let's say effect.
And then we can say on click.
So now in the example when I increment
the value you're going to see that on
click runs before the effect that is why
double is not updated. So we can see the
first thing that ran was the script
block then the effect when the component
was added to the DOM then we pressed on
click to increment count and then the
effect ran after it to update double.
The lesson here is that you should
always derive state instead. So we can
just remove this effect. We don't need
this. And instead of state here, we can
just use derived. And we can say count
times 2. And now everything should work
as expected.
Another reason why you might not need
effects is because of the onmount life
cycle function. So let's say that you
want to run something once and then run
a cleanup function when the component is
removed from the DOM. In that case you
can use the onmount life cycle function.
So we can say onmount and then we can
import it from self and we can just pass
a call back to it. So now we can say
console log
component addit. Let's use a wave emoji
and we can also return a cleanup
function. So we can say return
And we can say component removed. And
this is also great because you don't
have to worry about tracking some state
inside of an effect on accident. You can
also use the on destroy life cycle
function from swelt to run a cleanup
when the component is removed from the
DOM.
So we can import on destroy from swelt.
And then inside of here you can run
whatever cleanup you want. Also keep in
mind that you can put this life cycle
functions in other functions. So you can
import them in your component. They
don't have to be declared at the top
level of the script block.
All right. So when should you use
effects? Effect should only be used as a
last resort when you need to synchronize
with some external system that doesn't
understand swell's reactivity. For
example, fetching data from an API or
working with the DOM directly. All
right. So to show this, we can create a
simple PokƩmon search.
All right. So, let's create the search
query which is going to be Pokemon.
Let's say it should be Charizard by
default. And then we're going to get the
Pokemon image.
And then let's create the input.
So, this is going to be of type search.
Let's make this more readable.
And let's say placeholder should be
enter
Pokemon name. And then on input we're
going to update the search query. So we
get access to the event and then we can
say Pokemon
equals E target value. Now we just need
to add the image.
So the source is going to be image and
the description is going to be PokƩmon.
Let's also add a container.
And that's it. And now for convenience,
I'm going to copy paste this get Pokemon
function. So this is just using fetch
and the PokƩmon API to get the PokƩmon.
But now this makes sense to use an
effect because this is a side effect. We
want to rerun this function each time
Pokemon updates. So we can create an
effect
and we can just pass a function inside
of here. One common mistake I see people
do is pass a promise here. So for
example, if you say async, Selt is also
going to give you a warning because
these types don't match. So for example,
if inside of an effect you were to say
cones data await get PokƩmon, this would
actually work. But the problem is that
since you're passing a promise, your
cleanup function is never going to run.
So instead of passing a promise here,
you can reassign state inside of this
function get Pokemon and then you can
just say get Pokemon and that's it. Or
you can use an immediately invoked
function expression. So you just need
two round boys. Here is our logic. Then
we can invoke the function inside. And
you just need to pass a function inside.
And now you can make this async. But of
course this isn't readable. In this
example, we're just going to do get
PokƩmon.
Pass the PokƩmon. Then we can say then,
and we get the data.
And now we can update the image.
So we can say image equals data sprites
prompt default. So let's be more
responsible and not spam the PokƩmon
API. So one of the options that fetch
accepts is an abort signal that's going
to cancel the request. So we can pass it
as an option here. So you can say signal
and swelt provides a useful function get
abort signal which is going to cancel
the request as we type. So we can say
get abort signal is going to be
important from swelt. We can just invoke
it and that's it. So now in our example
we can see that we already fetch
Charizard. So we can try fetching
Pikachu.
So you can see it works as expected.
All right friends, one last thing I want
to talk about when it comes to effects
is the effect. Pre-rune. So for example,
you might need to do something before
the dome updates like measuring some
elements or the scroll position. In that
case, you can use the effect pre-rune.
For the sake of time, I'm going to copy
and paste this example here.
So, here we're using the gap animation
library and the flip plugin. So, we just
register the plugin and then we're going
to create 10 elements and then we're
just going to shuffle them. So, in the
markup, we're just looping over these
elements. Nothing special. And then
we're going to shuffle them.
In this example, I want to use the gap
flip plugin to animate the view changes
when we update state. So instead of
these numbers teleporting around, I want
them to smoothly animate their change in
position. For that to happen, we have to
record the previous and after state. So
let's create an effect.
All right. So whenever we shuffle the
items, we want to run this effect. So
let's add items as a dependency. For
this, we can just say items. All right.
Now we need to measure the elements
before the DOM updates. So we can say
const state equals flip
get state and then we can give it the
class of the item.
So this is going to be dot item. All
right. So now we can transition the
elements. So we can say flip rom. We can
pass it a state with some animation
options. We can say duration 1 second
stagger
0.01 second. And then we can specify an
easing.
So we can say power one in out. But as
you can see this isn't going to work. If
we save this for example and go back
here we can shuffle the item but nothing
is working. And that is because this
effect runs too late. We need to measure
these elements before the dome updates
and then we need to run flip after that
fact. So we can measure the elements
again and do the flip. So to do this we
can use the effect pre- rune. So, we can
just go here and say effect pre. And now
we can save this. But you're going to
see this still isn't going to work. So,
if we shuffle, nothing happens. And that
is because this all happens at once. We
need to measure the dome before it
updates. And then we need to measure it
again after it updates. So, we need to
somehow schedule this function. So,
there's a couple of ways that we can do
this. We can use request animation frame
or Q microtask. So for example, if I go
here and say Q micro test
and this is actually what Selt uses
under the hood to cue your FX but in
this case we can use this to schedule
this task to run. Now you can see when
we go here it should work
and SWEL actually provides a function
that does this for you and uses Q
microtask under the hood. So let me just
remove this and we can import tick from
Selt. So now tick is imported from Selt
and tick returns a promise. So we can
invoke it and we can say then
and you can do the same thing.
How cool is that? All right, so keep
this in mind if you ever need to do
something before the DOM updates.
So far we only use state at the top
level of the component. But we can use
state inside the functions and classes
including derives and effects. So let's
look at an example. Let's create a
function named create counter which
accepts a initial value. So let's create
some state inside of it. So we can say
count equals state and let's pass the
initial value and let's return count.
So we can create the counter
and let's create the button
and also let's increment the count.
But this isn't going to work. So if you
go to the example and try incrementing
count, nothing is going to happen. And
that is because count here is just a
regular value. So when we define a piece
of state with the state rune, this isn't
some magic reactive container. The value
of count is going to be the same value
as when it was evaluated. Sville doesn't
change how JavaScript works. So we
actually need some mechanism to always
get the latest value of count. So for
example, we can use functions. So we can
say count and then we can return the
latest value of count. To update count,
we can say set count. we can get the
value and then we can say count equals
value. But the downside of using
functions is that it's very verbose. So
for example, now we have to use
functions everywhere. We have to invoke
count here. It's not simple anymore. We
have to say counter set count. Now you
have to say counter count have to invoke
this and then we can increment it. All
right. So let's save everything. Now we
can try incrementing the count.
As you can see, it works as expected.
All right. But there is actually a
better way. Instead of using functions,
we can use getters and setters to create
property accessorers. Instead of this
count function, we can just say get
count. And instead of set count, we can
say set count. And that's it. So now
instead of doing all of this,
we can remove this. We can say counter
count plus. and we don't have to invoke
this anymore. So, let's save this and
let's try incrementing the count.
As you can see, using accessors is a way
cleaner syntax. All right, but even this
approach isn't perfect. And speaking of
magic reactive containers, we can use
deeply reactive state. So here, instead
of initial, we can pass in an object. So
this becomes a deeply reactive proxy. So
now we can say count and we can pass
initial. Now let's rename count to
counter. And instead of returning this
junk, we can just return counter.
So let's try incrementing the count.
As you can see, it works as expected.
All right. But if you want, you can
create your own reactive container. And
another reason you might want to do that
is because of type safety. So let's say
for example that you're using
TypeScript.
So let's create a reactive container. So
you can say function reactive but you
can name this whatever you want. Let's
say value and then let's create state.
So we can say state equals state and now
we can pass the value. So this can be
regular or deep state.
Let's return the state and that's it. So
now you can actually type this function.
So we can say t or the generic and you
can do whatever you want here.
So now we can just replace dollar sign
state with reactive and everything is
going to work the same as before.
All right, so this reactive utility
looks really useful. So why doesn't
swelt ship something like this out of
the box? Well, there are actually two
reasons. The first reason is because
it's only a couple of lines of code as
you can see. But another reason is that
swelt encourages you to use classes.
Because when you use state inside of
classes, it unlocks some superpowers.
So, let me actually remove this
and let's yink all of this. So, we can
create a class named counter. We're
going to create a constructor
same as the function is going to accept
an initial value. And the only thing
that we have to do is create this count
field. And we can say equals state and
pass initial. So let's create the
counter
new counter. Let's pass the initial
value. All right. So let's try
incrementing the count.
As you can see, it works as expected. So
what kind of black magic is going on
here? Well, let's actually look at the
compiled code. As you can see under the
hood, Swelt is going to turn this into a
private field and is going to create the
getters and setters for you. Of course,
at the end of the day, it doesn't matter
if you use functions or classes as long
as you understand how swelt reactivity
works. All right, so let me show you
another cool thing. Swelt has this
concept of universal reactivity. That
means that not only can we use state
inside of functions and classes, we can
also use it in a regular JavaScript
module. So let's do that. I'm going to
open the sidebar and inside of the
source folder, I'm going to say new
file. Let's create a lip folder and then
we're going to create the counter. So
you can say counter and the only caveat
is that the file name has to end with
swelt.js
or ts for TypeScript. So this way the
swell compiler knows that this is a
special file and it doesn't have to do
unnecessary work.
All right. So let's create the file.
So I'm going to close the sidebar and
let's just copy the code. So we can take
our beautiful counter.
Let's go back to counter.swwelljs.
We can paste the code in and we just
need to export it.
Now in app.swelt Well, we can just start
typing counter and it should autoimp
import from lib. All right. So now we
have our beautiful counter
and everything works as expected.
All right. So let's talk about something
very important and that is passing state
to functions and classes. So let's say
for example that we have a doubler
class. So we can say doubler.
We can create a constructor.
So we can say this count equals
we're going to derive count time two. So
let's create count using regular state
and then let's create the doubler. So we
can say let doubled new doubler and then
we can pass count. All right trick
question. Is this going to work? The
answer is no. And why it is? Well,
because as we learned before, count is
just a regular value. And we can
actually see this if we create the
button.
So let's say doubled count. Let's create
on click.
So we can say count plus+. And we can
look at our example. We can see that
nothing works. So again we have to use
some mechanism to get the latest value
of count. And usually that is a
function. So instead of just passing the
plain value of count, we can pass in a
function. So because this is a function,
we need to invoke this. Let's try
incrementing the count. And you're going
to see it works as expected. So we
learned that we can use deep state if we
pass an object inside of here for
example or we can use a reactive
utility. But in our case, we already
created a count class which is already
reactive. So we can use it. So we can
just say new
counter. So we can import it from
lib/counter.swelt.
We can pass in the initial value. And
now we can just pass the count
and this is actually going to be
counter. So we can rename this to
counter.
Let's say counter dot count. And here we
just need to change count to counter.
And we can just say counter dot count
times two. So we can derive the value.
All right. So let's try incrementing the
count.
All right. So now when you increment the
count, the doubled value is also going
to update.
You can also have reactive global state
thanks to swell's universal reactivity.
So for example, let's say that you have
a global config. So we can create a new
file inside of the lip folder. We can
name it config.swelt.js.
Let's close the sidebar and we can
export const config equals state and the
only thing you have to do is pass in an
object to create a deeply reactive state
proxy. So for example we can say theme
and of course it's going to be dark by
default and that's all you need to have
reactive global state. Of course you can
just use a class. So let's create a
config class. We don't even need a
constructor. We can just say theme
equals
state
and let's say dark. I'm also going to
add a method to toggle the theme. So
let's say toggle theme.
So we're going to say this theme equals
let's check what the current theme is.
So we can say this theme.
If it's light then we're going to set it
to dark otherwise it should be light.
All right. So now we can create a
singleton. So we can say const config
equals new config. And of course we
should export it. Now let's go back to
app.swelt. Let's create a button. So we
can say config. Let's import it. We can
say config. And now we can also add an
onclick event handler.
So we can say config toggle theme.
All right. So now we can toggle the
theme.
And there is only one gotcha with
classes. So we have to do something like
this inside of an anonymous function
because the context of this depends on
where it was called. For example, if you
just pass config.ggle theme, this isn't
going to work
because in this case this refers to the
button. But you can also use an arrow
function since it doesn't bind this. So
we can go back to the config. So we can
use an arrow function here.
All right, so let's see if it works.
Awesome.
All right, friends. So, let me show you
how you can avoid using effects. And I
honestly don't want to scare you from
using effects. If you sometimes use an
effect when you shouldn't, it's not a
big deal. It's not about performance or
anything like that. If effects were that
bad, they wouldn't be part of the
framework. Effects are necessary. The
problem is that it's easy to over
complicate your code with effects
because it seems like the right thing to
do. So let's look at an example how you
can make your life hell using effects
when you don't even need to use them.
All right, so let's use the same counter
class that we used before.
So we can say let counter equals new
counter
and then let's set zero as the default.
All right, let's create a button.
And then let's increment the count.
All right. So let's say that you maybe
want to read and write to local storage
when you update count. That sounds like
a side effect, right? So that's a
perfect use case for an effect. So let's
go to counter and inside of this
constructor, I'm going to copy and paste
this effects for sake of time. So here
we have two effects. one that is going
to read a value from local storage and
if it exists is going to update count.
The second effect is going to track when
count updates and is going to update the
value in local storage. So if we go to
the example and let's open the developer
tools, we can go here to application and
local storage. We're going to see that
the count is zero. So let's try
incrementing the count.
As you can see, it works as expected.
But let's see if the count value
persists when we refresh the page. And
it does. All right. So, what is the
problem? Well, let's go back to our
example. So, let's say that you have a
favorite counter that you want to share
with everyone. So, we can say export
cons counter equals new counter.
So, we can go back to app.2 12 and we
can import the counter. We can remove
this counter that we created and
everything should work the same as
before, right? Well, if we go back to
our example, we can see that we have a
bunch of errors. So, let's go to the
console
and we get this mysterious effect orphan
error. Effect can only be used inside of
an effect, for example, during component
initialization. What does this actually
mean? Well, your entire Swelt app is
actually a root effect and your
component is just a nested effect inside
of it. This way, Swell can keep track of
the effects and run the cleanup
automatically for you. So, what this
error is saying is that you're trying to
create an effect outside of that root
effect.
So, let's go back to counter.swell.
All right. So, this is how you enter the
first circle of hell. We can actually
create our own root effect. To do this,
SW provides an advanced rune called
effect.root.
So we can go here at the top. Let's say
effect.root. And now we can pass a call
back. So let's move effects inside of
the callback.
The major downside of effect.root is
that you have to do cleanups manually.
So for example, at the top we can
declare a private field called cleanup
and then we can assign it here.
All right. Then at some point you're
going to have a destroy method
and inside of it we can call the
cleanup.
As you can see this is starting to
become convoluted. So now let's enter
the second circle of hell. Let's remove
all of this.
We no longer need a root effect.
and let's remove this cleanup.
All right, so there is another advanced
rune that we can use and that is the
effect. Rune. This rune is going to
check if you're inside of a tracking
context like the template. So maybe
that's the solution. All right, let's
try it. So we can go here. Let's say if
effect tracking
and then let's also move effects inside
of it.
And if we go to our example, we can see
we have no errors. So let's go to
application
local storage. We can see count is 10.
That looks good. That's the default
value that we picked. So let's go up to
20. We can see the count increments, but
the value is never updated even if we
refresh the page. And why that is? Well,
that is because our effects are never
going to run because this counter was
created outside of a tracking context.
All right. So how can we start escaping
this hell? Well, it would make more
sense that we only create this effect
when we read the value and we can write
to local storage when we write to count.
So this effect is completely
unnecessary. So let's start by creating
a private field count
and then we can rename count to use the
private field count. And now we can
create the getter and setter.
All right. So let's go here and let's
say get count
and we can return this count. All right.
So we can do the same thing for setting
the count.
We get the value and then we can say
this count equals value. So now we can
yink this local storage line. We can
just place it here. We don't even need
this count. We can just say the current
value and then we can yink this effect
just delete it and we can copy this
entire effect where we read the count
value
and as you can see things are a lot
simpler. All right. So now when we go to
our example let's increment the count
and we should see that everything works
as expected. So let's see if the value
persists and it does. But there's
actually one problem. If we read this
count in multiple places, we're going to
create an effect. But we can easily fix
this. So we can create a new variable at
the top. Let's say first. And this is
going to be true. So now inside of the
effect, we can actually check if this
ran the first time. So we can say, hey,
if you already ran, then return.
Otherwise, we're going to retrieve the
value from local storage once. And then
we're going to set first to false.
So now we can increment the value and
everything works as expected.
All right, we've been through many
circles of hell, but we're still not
done. As you might have noticed, we
don't even need an effect here. So we
can just remove this effect.
And let's say if this is the first time
this ran
then we can run this
and we can set first to false. As you
can see this is much simpler. So you
should always avoid using fx. So now if
you go to our example and if we
increment the count we're going to see
that everything works as expected. Let's
see if the count value persists.
And it does. As you can see, this is why
you should avoid using effects. You can
do side effects inside of event handlers
like on click. So, always avoid using
effects if you can.
All right, friends. So far, I've been
constantly talking about signals, how
values are being read, and etc. But what
does this actually mean? So, in this
section, you can get the popcorn out
because we're actually going to learn
how signals work. So, let's look at this
example. So far we learned that we can
declare reactive state using this rune
and to update state we can use
reassignment but this equal sign isn't
anything special. If we look at the
compiled code we're going to see that
this just gets turned into a function
call. So this gets turned into set. So
we pass the signal and we pass the new
value. But how does this actually get
updated? And this is a true Scooby-Doo
moment because you're usually
discouraged from using effects in swel
but actually under the hood swelt
compiles this template into effects. So
you can see here is a template effect
and that is how swelt updates the dom.
So in this section I actually want to
implement a simple version of signals so
you understand how they work and swelt
isn't the only framework that uses
signals. You have angular solid quick
and so on. You're going to see that
signals aren't just hype, but they're
the perfect primitive for building user
interfaces.
All right, friends, let's pretend that
this is just regular JavaScript.
So, what is a signal? A signal is
basically a container that holds a value
and subscribers. So, we can create
function called state. Let's pass in a
value and let's create a signal which is
going to hold the value and subscribers.
So we're going to use a set. So the
subscribers are unique and then let's
return the signal. As you can see, a
signal doesn't do anything on its own.
For signals to be actually useful, we
need effects. So let's create an effect.
So effects accept callback functions. So
we can accept the function. And now we
need to track the active effect. So
we're going to say active effect equals
this function.
And then we're going to invoke the
effect. So let's create this active
effect.
By default, it's going to be null.
All right. So the actual magic happens
when we invoke the effect and we read a
signal. So let's create a function
called get. So we're just going to pass
a signal.
So now inside of get we can check, hey,
who called me? Then we can say signal
subscribers add the active effect and
then we can return the value. So you can
say signal do value. All right. So now
when we update the signal we notify it
subscribers.
So we can say function set we're going
to pass in the signal and a new value.
So we're going to say signal value
equals the new value. And now we can
notify the subscribers that the value
has changed. So we can say signal
subscribers
for each
effect. We can invoke the effect.
And that's pretty much it. And that's
really how simple a basic signals
implementation is. So let's actually use
it. In this case, since we're not using
swelt in particular, we're just going to
create a button. But of course since
we're still in SWEL I'm going to use
onmount to query the element
and then we can create the state. So we
can say let count equals
state. Let's pass the initial value. Now
let's query the button.
All right. So let's update the count by
saying button on click.
So we can use our set function which
accepts a signal. So we can pass count
and we need to use get to get the latest
value from the signal and we can
increment it and that's cool. But the
actual magic happens is when you use an
effect.
So again we're going to pass a call back
to this effect. So we want to update the
button text node. So we can say button
text content. we can say equals and we
have to say get count. So when we read
the signal, it's going to rerun this
effect. All right? So I hope you're
starting to understand how cool this is
because when this effect runs, this
callback is going to run. This is going
to become the active effect. Then we're
going to read the signal. And when we
read the signal, it's going to ask
itself, hey, who called me? Is there an
active effect? If so, let's add it to
our subscribers. And then when we update
the signal, we can notify the
subscribers. Of course, this is very
oversimplified, but this is a basic
example of how signals work. All right,
but let's actually see if this works at
all. So, we can save this. So, let's go
to our example and let's see if we can
increment count.
How beautiful is this, Rens? All right.
So, not only did we learn how signals
work, but we also learned that doesn't
compile reactivity. This is what it's
called runtime reactivity because this
happens each time you update a value. It
runs the signals and recreates the
dependency graph. The only thing that
swell compiles for is developer
experience because it hides this method
set get and etc. And that is the reason
why you can use signals like regular
values in swelt. As the react people
love to say it's just JavaScript.
All right, friends. So, I hope you
understand now why signals are so
powerful. Instead of having to do all
the manual setup to update values,
anyone that is interested about a value
updating can just subscribe to the
signal. Once you understand how signals
work, you're going to understand them in
other frameworks. And there's even a
proposal to add signals to JavaScript.
And now that you understand how
dependency tracking works in Swelt is
going to give you more confidence in
your own projects. And now you also
understand that this isn't some magic
under the hood where swell compiles
reactivity. It's just JavaScript. And
this is also important when you read in
the docs. So for example, here they
mention something that's called the
tracking context. And they say that's
the effect. So your user effects and
things inside your template. But what
they don't explain here is that your
templates get compiled to effects. So
that is the tracking context. And
speaking of effects, the ride values are
also effects. A derived value has its
own tracking context and it returns a
signal. Unlike user effects, when a
derived updates, it gets immediately
evaluated. All right, so I hope this was
helpful. All right, so let's talk about
using logic in your template. HTML
doesn't have conditionals and loops, but
in Swelt, we can use control flow blocks
and even blocks to deal with data
loading and promises. So let's look at
an example how we can conditionally
render content in Swelt.
I'm going to create a piece of state
called status
and this is by default going to be
loading. So this state can be loading
success or error. Now inside of our
template we can start by opening the
curly voice. We can say pound if and now
here we can use an expression. So we can
say status
does it equal to loading.
So we can use a paragraph tag and we can
say loading. All right. So let's close
the if block again. We're going to use
the curly boys slash if let's take care
of other state such as success and
error. So we can go here and we can add
another block. So we can use curly boys
again. We can say colon else if and now
we can use another expression. We can
say if status is equal to success. Let's
also use a paragraph. Then we can say
success. All right. So let's copy this
block over. So we're going to say if
status is error, then we're going to say
error.
We can also add another else block. So
we can say curly boys colon else and we
can say paragraph tag impossible state.
All right. So let's add some buttons.
Let's say loading. And then we're going
to add an on click event handler.
So we can say status equals loading.
All right. So let's copy this over a
bunch of times.
So we can say success.
Here we can say error.
And then here we can say other.
All right. Let's also add a container.
All right. So let's go to the example.
So let's go from the loading state to
the success state. And then we can go to
the error state. And if we go to other,
we should see impossible state. And that
is how we can conditionally render
content in SW.
All right. So let's look at how we can
loop over data in SW. So let's say that
you have a list of items such as to-dos.
We can say state. This is going to be an
array of to-dos.
So let's say ID1
the text is going to be to-do
and we can say done false. So let's copy
this over a bunch of times.
So now we can say to-do 2 three or we
can also bump the ID
and that's it. All right. So in the
template we're going to create a list.
All right. So let's use the curly boys.
Then we can say pound each in this case.
So now we can say each to-dos as to-do.
We can also close the each block by
using curly boys slash each. Now inside
the each block, we're going to create a
list item. Inside of the list item,
we're going to have an input and a span.
So we can say input type checkbox. And
then we can set the checked value. So we
can say checked equals to to-do done.
And then let's add a span for the text.
So we can say to-do.ext. text. All
right. So, we can add one more thing. If
the list is empty, so we can go here. We
can open the curly boys. We can say
else.
So, if the list is empty, we're going to
render this paragraph that's going to
say no items. As you can see, we
successfully iterated over the items. We
can also destructure the values from the
item we're currently iterating over. So
instead of to-do here we can use curly
boys and we can the structure ID text
and done. We can even get the index here
and you can name this whatever you want
like index but I'm just going to say I
and then we can also pass a unique key
like the ID. So this is as well can keep
track of changes. So we can use the
round voice and then we can just pass
the ID. In this case it's unnecessary
but in general it's a good practice to
provide a unique key so can keep track
of changes. All right. So now we have to
remove to-do dot and let's also do
something more fun with the index. So we
can color odd and even rows. So here we
have our span. We can use the style
attribute and we can say colon color.
So we can say I modulo to if there is no
remainder then we can say that the color
is going to be orange red otherwise
we're going to use the default color.
So now if we go to example you can see
that every odd row is colored.
All right. So let's say that you want to
loop over an arbitrary amount of items
like creating a grid for example. In
that case we can even omit the s part in
the each block. So let me show you. In
this example we're going to create a
10x10 grid. So let's say cells and then
we can start with the curly boys. We can
say each. And now we want to create an
array of 10 items. So we can just say
array and then we can pass the amount of
items that we want to create. So let's
close the each
and here we don't have to specify as
anymore. We're only interested in the
index. So we can name this index row.
All right. So now that we created the
rows, we can create the columns. So
let's just copy this code here
and we can rename row to call. Now let's
create a div. We're going to name this
cell and we can just output the row and
column number. So let's use curly boys.
We can say row
and then let's say column. As you can
see we created a 10x10 grid of rows and
columns.
All right. So what's really cool about
the each block is that you can iterate
over any value that supports array from.
So for example, we can create a map.
Let's say let items map. We can say new
map. Let's pass in some default values.
We can use an array of arrays to do so.
So we just need to pass a value. Let's
say apple.
Then let's copy this over. We can say
banana.
All right. So now let's create a set.
New set.
Let's pass in an array. So again, we're
going to have an apple and a banana.
And let's also create a generator. So we
can just say function and we can use a
star. So this is a generator. So we can
say items generator. And then we can
just yield the apple and the banana. So
we can say yield.
So we can yield the apple. And then
let's seal the banana.
All right. So let's loop over these
items. So now in the template, let's
create a list. All right. So this is how
we can loop over the map. We can use
curly boys each. We can say items map as
and then we can the structure the key
and the value.
All right. So let's close the each block
and then let's just output the key and
the value.
So we can say key colon value. All
right. So next let's loop over the set
which will be the same as looping over a
regular array. So we can copy this list.
Instead of items map we can say items
set
and then we can just say item.
All right. So the last one is going to
be the generator. So we can copy over
this block
and this is also really simple. The only
thing we have to do is invoke the
generator. So we can say items generator
and that's it. All right. So let's go to
the example. As you can see we iterated
successfully over a map set and even
generators.
Awesome. So keep that in mind. You can
loop over basically anything as long as
it works with array. Swelt even has
reactive versions of some built-in
JavaScript objects that we're going to
look at later.
All right, so let me show you a simpler
way of dealing with promises using the
await block.
So previously we fetched some PokƩmon
data using this get Pokemon function. As
you can see the function is mostly the
same. The only thing I changed is that I
explicitly returned the name and sprites
from the response. But in that example
where we used an effect, we didn't even
account for any loading success or error
state which becomes quickly tedious. So
to make that simpler, we can use the
await block in swelt to deal with
promises like a synchronous data
loading. So let's go to the template and
we can use the curly voice, we can use
the pound side and then we can say
await. If you want you can store a
promise in a variable or you can invoke
a function like this directly. So we can
say get Pokemon
and then let's say Charizard and then
let's close the await block by using
curly boys/await.
All right. So let's show the loading
state. So we can just say loading but
then when the promise is resolved we can
use the then block.
Let's use the curly boys. We can say
colon then and we can name the return
result. So this is going to be Pokemon.
All right. So inside of here, let's
create a container named Pokemon
details. And now we can create an image.
So the source is going to be Pokemon
image and the description is going to be
the Pokemon name.
And let's also add a paragraph here. So
we can say Pokemon name. All right. So
now we can also handle the error state
by using the catch block. So we can go
here and we can again use a curly voice
colon
catch
and we can say error. All right. So now
that we get the error we can display the
error message.
So we can say error dot message. All
right. So let's see if it works. And
awesome we got Charizard. All right. So
if you don't care about errors you can
omit the catch block and you can also
omit the den block if you only care
about the result. So let's just take the
markup.
We can delete this. We can paste our
markup here. And the only thing we have
to do is use then here. So we can say
then Pokemon.
And everything works the same as before.
Hey friends, I wanted to let you know
that there's an experimental async
feature that you can enable in Swelt. By
the time you're watching this video, it
might no longer be experimental. But
currently, if you enable this
experimental flag, you can use a weight
at the top level of your component
inside state and inside your markup. So
the only thing you have to do is enable
the async feature in your swel config.
And then you have to include this
boundary at the root of your app.
Basically, this is like react suspense
if you're familiar with that. And now
you can use await directly in your
component. So in this example, we're
adding A and B. As you can see, we can
use it directly in the markup. And you
can find more information in the
documentation like indicating loading
state. So you can use this
effect.pending rune. But yeah, there's a
lot more information in the SW
documentation.
All right. Sometimes you might want to
recreate some elements when a piece of
state changes. In that case, you can use
the key block. So let's look at an
example.
Let's create this value. I'm going to
set it at zero. And then we can create a
cute ghost. I mean a spooky ghost.
We're going to learn more about
transitions later, but for right now we
can just say in colon fate. And we can
just import fate from swelt /t
transition. All right. So now let's
create a button.
So now we can create a spook on demand
service by adding an on click event
handler.
So let's just increment the value. So
now we want to play the fade transition
each time value updates. So we can use
the key block for that. So we can go
here and we can use the curly boys pound
key. So now we can pass any expression
here like value and now we can close the
key block. So let's use the curly boys /
key and that's it. Now we can save it
and now we have a spook on demand
service. All right. So now when we click
on the button it should recreate the
ghost element and play the transition.
Spooky.
How spooky is that, friends?
All right, friends. The last cool thing
I want to show you is local constants.
Basically, local constants lets you
define variables in your template. So,
when would it be useful? Let's actually
look at an example. So, let me just copy
this example. So, this is the same
example that we used in the loop section
where we structured ID, done, and text.
But this approach kind of sucks because
now we lost the original reference to
the to-do, right? And what if we want to
use the to-do as a key here? Now we
can't anymore. We have to pass ID. Well,
we can actually fix this with local
constants. To create a local constant,
it has to be a child of any of the
blocks like if each and so on. And it
can also be a child of a component. So
in this case, we can just go here. We
can use the curly boys. Then we use the
at sign and then we can say const. So
now we can define a variable that is
scope to this block. But in our example,
we just want to structure these values
from to-do. So we can just take that and
we can say equals to-do.
Now we can just say to-dos as to-do. And
now we preserve the original reference
to to-do. So now we can pass that as the
unique ID. And we can also rename done
to checked.
And we're not using ID anywhere. So we
can just remove it actually. And we can
go here and we can just say checked. And
we already the structured text. As you
can see, local constants are very
useful.
So let's look at another example that
creates an SVG grid.
As you can see, we can define the size
and tiles in the script block. And now
we can pass them to the SVG. So now we
can combine what we learned before on
how to create a grid. So in this
example, we're creating a chessboard.
And now we can use local constants to
keep everything tidy and legible. And
this also helps us avoid creating
unnecessary components. And it's also a
benefit to performance if we compute the
value once if we use it in multiple
places. But in this case, we're just
creating the tile X and Y coordinates
with height and the fill. So we just
pass them here to W. And now we can look
at the example. As you can see, we're
creating this chessport. Local constants
are a very useful tool in your arsenal.
All right, let's talk about event
listeners in SW. So far, we used a
button with an on click event listener.
In swelt events are just regular
attributes. So you can say on and the
name of the event and you can pass an
inline function like this
and we can say console log click. So if
we open the developer tools and click on
the button, we should see click. And
what's really cool about events in swelt
is that they mirror JavaScript events.
So what do I mean by that? Well, you
might be familiar with something like
this. So where we have element add event
listener
and now we can pass the event listener
and we get access to the event. So we
can do whatever we want. But there's
also an alternative syntax where you can
say element on click and we also get the
event and we can do whatever we want.
And of course we also get access to the
event here. And instead of using an
inline function, we can create a handle
click function. So let's take this and
we can name this handle click.
So let's remove this yoink. Let's name
this function handle click.
And we get the event implicitly. So we
don't have to pass it. And then we can
just put our console log here. And now
if we click on the button, we should see
click again. And also because events are
just regular attributes if they have the
same name then we can use a shortand. So
in this case we can rename handle click
to on click and if I save and format the
file is going to use the short hand and
because events are just regular
attributes we can also spread them. So
let's take this console log here and
let's create this object called events.
So you can have on click
and then let's copy it over. And this is
going to be on double click.
And then we can just spread the events.
So now when we click on the button,
we're going to get click. And if we
double click the button, we're going to
get double click.
All right, let's do a more fun example
where we track the mouse coordinates
using the on mouse move event.
So we need to track the X and Y mouse
coordinates. So let's create mouse
equals state and then let's pass in an
object here. So we can say X is equal to
zero and the Y coordinate is also going
to be zero. So let's create this
container. I'm going to use this mouse
class. So this container is going to
take the entire width and height and
then we can say the mouse position is
and we can use the coordinates. So we
can say mouse x
and mouse y. All right. So let's create
the function to track the mouse
coordinates.
So let's name this on mouse move. Then
we can accept the event. Now we just
need to update the mouse position. So we
can say mouse x equals e client x and
let's also update the y-coordinate.
So mouse y equals client y. And that's
it. Now we just need to add the event
handler. So we can just go here and we
can use the curly boys and let's just
say on mouse move and we're going to get
an accessibility warning, but we can
just ignore it by clicking on quick fix
and then we can disable it.
All right. So let's go to our example.
And now we can see that the mouse
position is being tracked. How cool is
this? And that's how simple that was.
All right. So in the last example, I
want to show you that you can actually
use forms to get the user submitted data
instead of always using state. So let me
show you. Let's create a form. It's not
going to have any action because this is
going to be purely client side. When
you're using cell by default, you're
building client side applications. So
everything is handled on a single page
using JavaScript. So let's say for
example create an input which is going
to be email. So to get the data from the
form we have to give it an identifier
such as a name. So we can say email and
then let's create a submit button. So we
can say type submit.
So for example this might be your
newsletter. So we can say subscribe. All
right. So usually where you would use
state to get the email we can actually
just create a function and we can listen
to the onsubmit event. So let's say
function onsubmit. We get the event as
the argument. And now this is the most
important part. We have to prevent the
default behavior because the form is
trying to post to the server. To avoid
that we can use event prevent default.
So now we can control the form using
JavaScript and avoid a page reload. All
right. To get the form data we can say
cons data equals new form data and we
can pass the form. In this case we can
just use this to reference the form
element. And then we can say con email
equals data get. And this is where our
unique name identifier comes in. So we
can say email and that's it. So now we
can console log the email.
All right. So now we can go to the form
and we can use onsubmit.
All right. So let's enter an email like
test.com
and let's open the developer tools. And
now we're going to see if we subscribe
is going to output the email. As you can
see, sometimes a form can be enough and
you don't need any extra state.
All right, let's talk about data binding
in SWEL. So here I have a special
example for you. I'm going to copy and
paste for sake of time, but it's really
just a simple search. Here we have a
list of JavaScript frameworks. Here
we're using a derive to filter the list
based on the search. And this is how we
update the search using this input. So
we assign the value to search and then
we use on input to update the value. And
here we're just looping over the list.
Nothing special. All right. So for
example, if we type in s now we're going
to get solid and swelt. And of course in
the example I would also make this to
lowerase where you have item and search.
So it's case insensitive. All right. So
in previous examples where we used
events like on input, there were
probably some people yelling at the
screen that already know SWelt. What are
you doing? This can be so much simpler.
And yes, that is true. This is part of
the plan. What we're doing here is
called one-way data binding because
we're using the input to update some
state in our application, but the state
in the application doesn't reflect in
the input. What's worse is that you do
something like this all the time. So,
this is just boilerplate. Thankfully,
Swelt supports two-way data binding
using the bind directive. So let's see
how we can use the bind directive to
improve this experience. All right, so
let me show you how cool this is. So we
can just delete all of this junk and we
just need to say bind colon value and
what do we want to bind it to? So we can
say search and that's it. Now we can
save. And now if we go to our example,
we can see that it works the same as
before.
And swell has many writable and readable
bindings that you can use. Let me show
you one of the more useful bindings
which is going to be bind colon this to
get a reference to a DOM element. So
let's look at an example. Let's say that
we have a canvas and we can give it some
styles.
So let's say canvas width 200 pixels by
200 pixels.
And then let's give it a white
background. And now we have a simple
canvas with a white background. All
right. So let's say that we want to
reference this canvas. Maybe we can go
inside of here and create an effect
and then we can say const canvas
document query selector and let's select
the canvas and then we can get the
context. So we can say const ctx equals
canvas get context
2D. And if we log the context,
so if we open the developer tools, we
can see that it works. But the downside
of this is that you're targeting any
canvas in your app. So you could have
multiple canvases and etc. So you
actually don't know what you're
selecting. So instead of doing this, we
can remove this line and we can go here
and we can say let canvas. And this is
going to be undefined until your
component is added to the DOM. So now we
can go to our canvas element and we can
get a reference to it by saying bind
colon this. And now we can bind it to
canvas.
As you can see now we get a reference to
the canvas. And of course instead of
using an effect here if you don't care
about tracking any values you can use
onmount
and you're going to get the same result.
All right. So let me show you another
cool feature when it comes to bindings.
Let's say that you want to make a
mocking Spongebob case converter. So in
this example, I have a simple function
which is called to Spongebob case which
accepts some text and then it converts
it to the mocking Spongebob case meme.
And here is our state. All right. So how
do we make this work? Well, we learned
that we can bind a value. So we can say
bind value and then we can just maybe
pass text. But we have to convert the
case. So maybe we can pass a function
here. So we can say to Spongebob case
and then we can pass text and we
immediately get an error because we
can't bind to a function of course. All
right. So maybe you're thinking let's
create a derived. So we can say mocking
spongrol case
and then let's say derived. So we can
say to spawn case and you can pass in
the text. All right. So let's go here.
So maybe this is going to work. Well,
let's save it and we can go to our
example. So it seems that it works at
first, but we're going to see if we type
in anything else, nothing is going to
work. And that is because we can't bind
to derives. All right. So if we can't do
this, then maybe we need to go old
school and just use the regular
attributes. So we can go here for
example and say value
So you can say to Spongebob case. Let's
pass the text and then let's use the
event listener on input.
So we get access to the event. And now
we can say text equals e target value.
All right. So let's see if this works.
And if you go to our example and start
typing. Awesome. You're going to see it
works. All right. But there's actually a
better way to do this using function
bindings. So what are function bindings?
Well, instead of binding to the value,
we can pass a get and set function. So
we have complete control what happens
when you read and write to a value. So
here is how it looks like. Let's just
delete all of this. We can say bind
colon value. But now we can pass a get
and set function or we can just write it
in line.
So let's define a getter.
So we can say to spawn case we can pass
the text and now we can set the value
when we update it. So we can say value
and now we can reassign the text. So we
can say text equals to Spongebob case
and we can pass the new value and I'm
also going to wrap this in curly boys
because of formatting.
All right. So let's go to our example
and let's type test. And we can see it
works. So now we can let everyone know
that React is just JavaScript.
All right friends, let me show you some
cool bindings that you can use. Among
the many bindings, you have awesome
media bindings for audio, video, and
image elements. Let's look at the video
player. So in this example, I have a
simple video player. Right now, it
doesn't do anything. So if you look
here, here we have a reference to the
clip. And then we're going to bind the
current time, duration, and the pause
state. In the markup, I just have a
simple video element, some controls.
Here we have the time and the timeline
is just a range input. So, we're going
to bind the value of the current time.
The max value is going to be the
duration. And here we have the step. All
right. So, let's go to the video element
and let's bind some values. So we can
bind the current time,
the duration, and the pause state.
All right. So now we can go to the
player. Let's see if playing the video
works.
Of course, we can also scrub through the
video.
Awesome. And if you want to learn about
all of the values that you can bind, you
can go to the SW documentation. As you
can see, there's a lot of them.
All right, I want to show you one last
cool example.
In this example, I have this content
editable area so we can edit the text.
And let's say that we want to measure
when its width and height changes.
Usually, you would use something like
the resize observer API, but SWEL
provides read only bindings for the
client width and client height. So here
we defined width and height and now we
just need to bind the value. So we can
go to text area and we can say bind
client width and we can bind it to width
and we can also bind the client height
and that's it. Now we can go to the
example and we can type something.
As you can see, it's that easy.
All right, friends. In this section,
we're going to learn everything about
components. First, we're going to create
a basic to-do app, and then we're going
to split it into multiple components so
we can learn how we can pass data around
and so on. Let's start by creating a
form to add the to-do.
All right. So, let's add an input and a
placeholder.
So, we can say add to-do. And then we're
going to bind the value
to to-do. So let's use the onsubmit
event.
And we can create a function that's
called add to-do. All right. So let's
add some state. So the first piece of
state we're going to need is of course
going to be let to-do.
And then we can copy it over and create
to-dos. And this is going to be an array
by default. All right. So let's create
this add to-do function.
And of course, we get the event
implicitly. So, we want to prevent the
default behavior. So, we don't reload
the page. And then we can just push the
to-do to the to-dos array. So, we can
say to-dos push. So, let's pass in an
object. So, first we're going to create
an ID. So, we can use crypto from window
to get a random UU ID. And then we're
going to have text, which is going to be
the to-do text. And then we can say
completed is false by default. All
right. So now let's loop over the
to-dos.
So let's create a list. Then we can use
the curly voice. We can use the each
block. So we can say each to-dos as
to-do. We can pass the to-do as the
unique key. Let's close the each block.
And then here we can just create a list
item.
All right. So we can create a checkbox.
So we can bind the checked value
to to-do completed. So let's add another
input which is going to be our to-do. So
we can bind the value to to-do text. And
let's also add a button for removing the
to-do.
So we can say on click
we're going to create this function
called remove to-do. And we can just
pass the to-do. All right. So now we can
go here in the script block under add
to-do. Let's add this function.
Remove to-do. It's going to accept the
to-do. And now we can reassign to-dos to
equal to-dos filter. So we get the
to-do.
And then we can say if the to-do ID
doesn't equal to the to-do ID that we
passed, then we're going to filter the
items based on that. All right. And the
last thing I want to do is add a
container here for styles.
So we can say to-dos
and that should be it. All right. So
let's see if we can add any to-dos here.
So we can say learn swelt.
As you can see it works as expected. All
right. But let's add a simple
transition. We're going to learn more
about transitions later in the course.
But for right now I'm just going to go
to the list item. Then we can say
transition colon. So we can type slide.
So let's press enter to import slide
from swelt /transition.
As you can see slide is imported from
swelt /transition.
All right. So now if we go back to our
example, let's type in learn swelt. Now
you're going to see this beautiful
transition.
And that's how simple that is. All
right. So let's fix one more thing. So
for example, if we add a to-do like
learn, you're going to see the input
doesn't clear. So let's clear the input
by going to add to-do and then we can
just clear the to-do value. All right.
So now we can say learn swelt
profit and as you can see now the input
is cleared and let's also remove the
to-do so we can see our animation
working and removing the to-do works. So
you can see it looks great. All right
let's show the remaining to-dos. So we
can say let remaining. So this is going
to be a derived piece of state based on
when to-dos change. So we can create
this function remaining to-dos
and we can create this below remove
to-do.
So we can say function
remaining to-dos.
All right. So we basically want to
filter the to-dos and return the length
of the array. So we can say return
to-dos filter. So now we get access to
the to-do and then we can say if to-do
is not completed. But of course we want
to return the length.
So below the list we can create a div
and let's create a paragraph tag. So we
can say remaining and then let's use an
expression. So we can say if remaining
is one
then we can say item otherwise is going
to be items and then we just need to add
left. All right. So now let's add a
to-do.
All right. So, let's delete that to-do.
As you can see, everything works as
expected. All right. But I also want to
be able to filter the to-dos by all
active and completed. And I also want to
have an option to clear the completed
to-dos. So, back in the code, let's
create this filters div. And let's use
the curly boy. So, we can create an each
loop. And now, let's use an array
directly. So we can define our filters
which are going to be all
active and completed. So we can say s
filter. Let's close the each block.
All right. So let's create a button.
So we can say the name of the filter.
And now we only have to update the
filter. So we can say on click.
So let's say set filter. And then we can
just pass the filter. And then the last
thing we have to do is add the button to
clear the completed to-dos.
So let's add a button here. Then we can
say clear completed.
And then we're going to create another
function named clear completed.
All right. So now let's go back to the
script block.
And this looks like a good place to add
our functions. So let's say function set
filter is going to accept the new
filter. So we can just say filter equals
new filter. And of course we need to add
this piece of state.
So let's say let filter
state and by default is going to be all.
All right. So now we only have to clear
the completed to-dos.
So we can say function clear completed.
So let's say to-dos equals to-dos
filter. Now we get access to the to-do.
Now we can filter the to-dos that aren't
completed.
All right. So let's add our to-do. Learn
swelt profit. What you're going to see
if we complete this and we try using our
filters, it won't really do anything.
And that is because in our code here,
we're looping over to-dos. What we
actually want to loop over are the
filtered to-dos. So let's go to the top.
So here below filter, let's add another
piece of state. Let's say filtered
to-dos equals derived. So let's create a
filter to-dos function. So let's keep
everything organized.
All right. So, let's create the filter
to-dos function. Let's find a cozy spot
here. Looks perfect.
We can say function
filter to-dos.
All right. So, we just need to filter
the list of the to-do items. So, we can
return to-dos filter.
And now we can say if filter
equals all then we can return true. So
let's copy this over two times. We can
say active
and completed.
So here we can just say isn't completed
and then for completed we can say
completed.
All right. So now we can replace to-dos
with filter to-dos.
So, let's go to our example.
And now, let's add a to-do.
Let's complete the first to-do. So, now
let's sort the to-dos by active. As you
can see, it works. So, let's sort them
by completed and let's sort them by all.
So, now let's see if clear completed
works.
Awesome. Everything works as expected.
All right, friends. Let's learn about
components by taking the contents of
this file and splitting it into multiple
components. So, let's open the sidebar
and inside the lip folder, let's create
a new folder called to-dos.
And we already learned that swel
components end with the dotswelt
extension. So, for example,
to-dos.swelt. But the casing doesn't
matter. This can be lowerase or
uppercase. The name of the component
itself doesn't matter as long as it ends
with a dotswelt extension. All right.
So, let's oneshot all of the components
that we're going to need.
So, let's create add to-do.
We're going to have a to-do list,
a to-do item,
and a to-do filter.
All right. So, let's close the sidebar,
and we can close all of these files. All
right. So let's start with the add to-do
component. So we can go here to our
form. We can cut this content. And to
use a component, we start with the
pointy boy. And then we can type
component. And in this case, the casing
is important. The component name has to
be capitalized or use the dot notation.
For example, my component. But in this
case, we want to import the add to-do
component. So let's say add to-do. As we
start typing it, if we press enter,
we're going to autoimp import it at the
top. All right. So how can we pass
things to this component? Components
like regular HTML elements also have
attributes but for components we call
them properties or props for short. So
what we want to pass to add to is the
add to-do function itself and the to-do.
All right. So let's go to the add to-do
component
to accept props inside of a component.
We can just say let props and then we
can use the props rune.
And now we can paste our content.
Now we can get add to-dos from props.
And we can get to-do from props. Of
course, this is tedious. So we can
dstructure them.
So we can say add to-do and we can say
to-do. We can also specify the default
value as a fallback.
And if you have any other props, you can
just gather them and you can spread them
on your element. So we can do something
like this. And this is really useful if
you're making something like a component
library. You're making a button. So you
can pass whatever you want. But in this
case, we're not going to use it. So let
me just remove it.
And of course, let's remove props.
All right. So let's add a to-do. So we
can say learn. But you're going to see
nothing happens. And why that is? Well,
basically we're mutating this to-do from
a child component. So we have to tell
swelt that to-do here is bindable and
that it's okay for the child to mutate
parent state.
So you can use the bindable rune. So we
can say to-do equals bindable. And if
you want, you can also pass a fallback
here. All right. So now that we've done
that, we actually have to go back here.
And now we can bind the value of to-do
and we can remove this.
All right. So let's add a to-do. As you
can see, it works beautifully. What's
also really cool is that you can bind
the instance of this component using
this and you can bind it to some
variable like element. And now inside at
to-do if you have any function like
example and you export this function now
you can actually access it where you
bound it. But in this case, binding the
property isn't even necessary because we
can move state inside add to-do. So we
can delete to-do
and let's just add a state here. Let's
say let to-do
state and that's it. So now we can
invoke add to-do. We can create a
function onsubmit.
So let's remove add to-do here.
And of course we need to prevent the
default behavior. So we can say e
prevent default.
And now we can say add to-do. And we can
pass our to-do. And let's clear the
to-do. All right. So we can save this.
Let's go back here to our component. We
no longer need this. Now we need to
update the add to-do function to just
accept a to-do.
So this is going to be to-do. We can
delete this line
and this one. All right, that should be
it. All right, so let's add a to-do.
As you can see, it works the same as
before.
Let's also do a bit of cleanup. So, we
no longer need this piece of state. And
I like to keep the import separate. All
right, so next, let's work on the to-do
list. So, let's go to our markup. Let's
take this list
and let's go to to-do list. All right.
So, let's add our markup.
Then, let's create a script block. And
now we can accept some props.
All right. So, we're going to accept
to-dos and remove to-do. So, in this
case, we can replace filter to-dos with
a more generic to-dos because we can
pass anything, right? So, let's just
import slide. And that's it for this
component. All right. So, we can go back
to our main component. So now we can add
to-do list here. Going to autoimp
import. So now we just need to pass the
to-dos. But keep in mind these are going
to be the filter to-dos. And we just
need to pass remove to-do. All right. So
let's see if this works.
Awesome.
All right. So let's take this markup and
this is going to be our filter to-do
component.
So let's go to filter to-do or to-do
filter. So let's add some state. All
right. So let's dstructure these props.
So we're going to pass remaining
set filter and clear completed.
All right. So back in our component, we
can say to-do filter.
And now we can pass the remaining to-dos
the set filter function and clear
completed.
All right. So let's go to our example.
Let's add some to-dos.
So let's see if our filtering works. And
it seems it does. Beautiful.
So let's do some cleanup. We can remove
this slide. And I think that's all.
Let's see.
Yeah. All right. The last component is
going to be the to-do item. So, we can
go to to-do list. And now we can create
a to-do item. So, let's yink this
markup. And then let's go to to-do item.
And we can paste it right here. So,
let's get the props.
So, the props are going to be the to-do
and remove to-do.
Let's also add slide here. Save
everything. And now back in this
component,
we can say to-do item. So we're going to
import it at the top.
So let's pass that to-do
and remove to-do.
We can delete slide. And that's it. So
now let's add some to-dos.
We can see that everything works as
expected.
All right. So the last thing to do is to
go to app.swelt. Now we're going to take
everything from here. So let's go to
to-dos.swelt
and let's put our code here.
And now we can update this imports. This
is no longer lip to-dos. We can just
delete this part. All right. So now we
can go back to app.swelt. Let's delete
everything. And now we can use the
to-dos component.
All right. So let's do one last check.
We can complete the to-do. We can filter
the to-dos
and we can clear them. Awesome. All
right. There is one last thing I want to
show you. So for example, it's really
troublesome how we're mutating state so
deeply in this child component. So if
you go to to-do list, now we're mutating
from this child where we have to-do
item. And this can get you into trouble
if you're not aware of it. In my
opinion, binding values is perfectly
fine one or two levels deep max. But it
can get out of hand if you have more
nested components. In that case, instead
of binding the values, we can just pass
regular callbacks. So let's actually do
that quick refactor just to show you how
it looks like.
So back in to-dos, we're going to update
at to-do to work the same as before when
we had an event. So now we have to say
event prevent default.
So let's go pure JavaScript and we're
going to get the data from the form. So
we can say const form data equals new
form data and we can pass it this which
is going to be a reference to the form
inside of add to-do. So here we can just
say form data get
to-do and then we can clear the form.
All right. So, because we're not going
to bind the values in the to-do item
component, we actually have to create
functions for toggling and updating the
to-do, but it's nothing complicated. So,
let's go here and let's create our
functions. So, let's say function toggle
to-do. We're going to accept the to-do.
First, we need to get the index of the
to-do. So, we can say to-dos find index.
So, we get the to-do item and then we
can say to-do ID equals to-do ID. All
right. So now we can update the
completed state of the to-do item. So we
can say to-dos pass in the index
and we can change the completed value.
All right. So the update to do function
is going to be similar. All right. So
let's create the update to-do function.
So let's say update to-do.
All right. So we also get the to-do
index but in this case instead of
completed we just want to update the
value. So we can say text equals
to do text and that's it. So now we just
need to update the props that we're
passing. So add to-do is going to be
unchanged. But for the to-do list now
besides passing to-dos and remove to-do
we're going to pass toggle to-do and
update to-do.
And let's see what is this complaining
about. is because we didn't pass the
prop. That's fine. And to-do filter is
going to remain the same. All right. So,
let's update add to-do. And isn't this
nice, friends? Deleting code sometimes
feels better than writing code. So, we
can just delete all of this junk.
Since we're not going to use bind now,
we just need to give this an identifier
using name. We can name it to-do. And of
course, on submit, we're going to invoke
add to-do.
And that's it for the add to-do
component. All right. Now, we just need
to update to-do item. Inside to-do list,
we're going to accept toggle to-do
and update to-do.
So, let's pass those as props.
All right. Now let's update to-do item.
So let's get to
and update to-do.
All right. So this is going to be a bit
more verbose because we're not binding
the value. So we have to say that the
checked value is equal to to-do
completed. And now we can use an event
listener on change.
So we can say toggle to-do and then we
can pass the to-do. So we can do the
same thing for the text value is going
to be to-do text and we can use on input
and we can say update to-do and we can
pass the to-do. All right. So let me
save and format. So let's try adding a
to-do.
The only thing that's not working is
clearing the input. So let's investigate
that. So, we can go here to our
component where we have add to-do.
Oh, my bad. Clear isn't a thing. This is
actually dot reset. All right. So, let's
add a to-do.
So, let's see if we can complete a
to-do, if we can filter them,
and let's see if we can clear the
to-dos. Awesome.
As you can see, bindings are really
useful, but in most cases, you can just
pass in regular events. There's nothing
wrong with that, especially if you have
deeply nested components. And later,
we're going to learn how we can
communicate between components without
using props or events.
All right, friends. Now that we know how
components work, let's talk about
component composition. Let's learn how
we can compose components in Celt by
using nesting and snippets to render
children and how to communicate between
components without using props or events
by making a completely customizable
accordion.
So in HTML we take it for granted that
we can just nest elements and we can
also slot content like in this accordion
item. So let's see how we can translate
these concepts to swelt. All right. So,
let's open the sidebar and let's create
a new folder called accordion.
So, let's oneshot the accordion
components. So, we can create
accordion.swelt.
And we're also going to have an
accordion item.
And let's also create index.js so we can
export our components.
So, let's close the sidebar. All right.
Our goal is to be able to use the
accordion items like this. So we can say
import accordion and accordion item. And
for this we have to individually export
the components. So we can say export
default as accordion
from.
And now we can say accordion.
So let's do the same thing for the
accordion item.
And that's all that we have to do. All
right. So now in app.swel we can import
the individual accordion components.
So we can say import
accordion and accordion item from
lib accordion. In this case the index.js
file is going to be automatically picked
up. All right. So let's use the
accordion component.
And inside of the accordion we're going
to have our accordion item. All right.
But how can we render these children? By
default, any content between these
component tags is going to automatically
become part of a children snippet. And
snippets are just regular functions that
let you create reusable markup and let
you delegate rendering to child
components. So in this example, this is
actually a snippet. So we can create a
snippet by oursel. If we define the
snippet here, we can say children. Let's
close the snippet.
And the great thing about snippets is it
can be anywhere in your component. So we
can have it here. And now we can pass it
as a prop to this component.
But if you define a snippet between
these component tags, then it's going to
become a prop implicitly.
So we can remove this and we don't even
need to define a children snippet
because it's already done for us. All
right. So let's go to the accordion
component and let's render the contents.
So we can destructure children from
props.
And now let's create an accordion
element. So we can use an if block to
check if the children exist.
And to render a snippet we use the
render tag. So we can use curly voice at
render. So you can say children and we
can invoke the function. And we can also
provide a call back. So let's say else
and now we successfully rendered the
children. All right. Instead of using
this if block we can also use optional
chaining.
So again we can use the curly boys at
render
children question mark and then we can
invoke the function. So this is a
shorter way of doing it. As you can see
it works the same.
All right. So let's go back here and I'm
going to remove this. So let's use an
accordion item instead since now we can
nest anything. So we can say accordion
item.
All right. So let's pass some content
here and let's create a title prop.
So we can say item A. All right. So
let's go to the accordion item and we
have to do the same thing. So we can the
structure children and we also have a
title.
So let's create an accordion item.
We're going to have a button which is
going to have a name accordion heading.
So let's create a div inside of it.
We're going to pass the title and let's
create an accordion trigger.
So let's use an emoji and then let's add
an on click listener to the button.
We can name it toggle. So let's go back
here and let's create a piece of state
called open.
By default it's going to be false and
let's create this toggle function.
So we can say open is opposite of open.
So we're just going to flip the value.
All right. So below the button we're
going to render the children content.
So let's say if open
let's create a div name accordion
content and then we can render the
children. So let's use a curly boy at
render
children.
And I'm also going to add a dynamic open
class to the trigger. So you can say
class open.
All right. So we can go to our example.
Let me bump the size up. And we can see
that it works beautifully.
And that's basically it. We can add
multiple accordion items. So let's
create four of them. Let's say item B,
C, and D. And now we can go to our
example. And we can see that they all
work.
That being said, this has limited
composibility. And what do I mean by
that? Well, let's say, for example, that
your user now wants to change the icon.
So you're going to add an icon prop and
then they can pass an emoji or whatever.
But maybe they also want to change the
icon position. So now you create another
prop because you want to please the
user. So you say icon position. Okay.
All right. So you have all of these
options. But that's not a way to live
your life. Instead of doing this, you
should use inversion of control. So you
should let the user control how they
want to render this content. Unless of
course you want a stricter API like
this. All right. So let's remove this
and let's go into the accordion item
component. All right. So instead of
passing children and title, we can pass
a snippet. So let's pass an accordion
item snippet. So now we can go to the
markup and we can just yank it out of
here.
And here we can render the snippet. So
we can use the curly voice at render
accordion item.
And because snippets are just functions,
we can pass open and toggle.
And now we have complete control how we
want to render the accordion item. So we
can just remove this prop since we don't
need it anymore. Let's remove the
content.
And now we can just pass a snippet. So
we can say curly boys cache snippet. So
the snippet name was accordion item.
And now we can destructure the open and
toggle state from the accordion item.
So let's close the snippet.
And inside of the snippet, we're going
to paste the code that we copied.
So now we can remove this title here
and say item A. And of course, we're not
rendering any children. So this is our
content.
And that's it. And as you can see now we
have complete control how we want to
render the accordion item. Of course you
can have both in the accordion item. You
can conditionally render by default the
prop the user pass or a custom snippet.
All right. So let's look at our example.
It works the same as before but now you
have complete freedom how you want to
render this accordion item. So for
example maybe want to move this icon
here. We can just render it like that.
All right friends, let me show you
something really cool and that is how
you can communicate between components
without using props or events. So let's
say for example that you want to control
the open and close state of every
accordion item.
So for example, we can declare a piece
of state here by saying open and then
let's say false. But here's the problem.
Are we going to pass this piece of state
to every accordion item? That seems
really annoying. And if you for example
bind this value, how are all of the
children components going to get
notified that this value changed? Well,
for this we can use the context API. So
let me just remove this. I'm going to
keep bind open. So let's go to the
accordion component. We're going to
accept this open prop. But remember, we
have to make this bindable.
All right. So now in the parent
component, we can set the context. So we
can import the set context function from
swelt. And now we have to give the
context a key. So we can say accordion.
And now we can pass in here whatever you
want including reactive state. But keep
in mind just passing open isn't going to
work. And that is because open is just a
regular value as we learned before. So
in this case open is just going to
evaluate to false and that's it. It's
never going to update. We need to use
some mechanism to get the latest value
of open. So you can use functions or
classes but in this example I'm just
going to use a property accessor.
So I can just say get open
and then we can return open.
So now that we defined the context in
the parent component we can access it in
any child component and in this case
it's going to be accordion item. So we
can go to the top here. All right. So
let's get the accordion context. So we
can say con accordion equals and now we
can use the get context function from
SW. So we can import that and now we can
access the context by passing the key
that we defined before
which is accordion. All right. So now we
can turn this open state into a derived
and we can listen when accordion.open
changes because it's reactive. All
right. So now let's go back
and now here we can create a button.
let's say button outline. So let's use a
curly boy and an expression inside. So
we can say if the state is open then
we're going to say close. Otherwise
we're going to say open. So let's add on
click handler.
And then we can just flip the value of
open.
All right. Let me copy the accordion
item multiple times.
All right. So now when I click open, all
of the accordion items should be open.
So now if I close them, they're all
going to be closed. Most of the time
you're going to want to pass props. But
when you have deeply nested component or
you're making something interesting like
this accordion component, then the
context API is a perfect solution.
All right. So there's one more thing
that I want to show you when it comes to
component composition, and that is the
module script block. It's easier to show
you. So I'm going to create a new file.
So let me press control shiftp and then
we can change the language mode. So we
can change this file to be javascript.
All right. So essentially cell
components are just regular functions.
So for example we have a function called
component
and then let's say inside of it we want
to create a unique ID.
So we can say crypto random yuyu ID. And
that's great if you want a unique ID for
every component instance
because when we create a new component,
we're going to get a new random ID. So
let's say that we have four instances of
the same component. So we can say let A
equals component.
So we have A, B, C, and D. And now every
component is going to have a unique ID.
But let's say that you want to share the
same unique ID across component
instances. Well, this file is actually
called a module. So instead of creating
a unique ID inside of a component, we
can move it at the top of the module. So
let's just move ID.
And now it doesn't matter how many
component instances you have because
they're all going to reference the same
unique ID. So that is what the module
script block is for.
All right, let's look at an example. So,
let's open the sidebar and then let's
collapse to-dos and accordion. And now
we're going to create a new component
inside the lip folder named
counter.swelt.
So, let's collapse the sidebar. All
right. So, normally we would create a
script block and we would say count
equals state zero.
And we can create a button say count
on click
count ++. But let's say that you want to
share this count state across component
instances. So instead of using a regular
script block, we can just use a module
script block. So we just say module and
that's it. And of course you can have
both of them in the same component. So
you can have a regular script block and
a module script block.
But this isn't only useful for sharing
data and state. You can also export
utilities and whatever else you want
including snippets. So for example,
let's add a function that can reset the
count.
And then we can say count equals zero.
All right. And let's say you have some
snippets. So here I have an icon
snippet. And this is a really great use
case for snippets. You can define your
SVG icons inside of a component and then
you can export them. The only caveat to
when exporting snippets is that they
can't reference any state inside of a
script block. So they have to be
self-contained. And remember snippets
are just regular functions
and we can say export
icon. So you can export as many snippets
as you would like. All right. So now
inside app.swvel So we can create a
script block and we can say import
counter and let's import icon and reset
from
lib/counter.sswelt.
All right. So let's use the counter
component
and let's create a reset button.
And let's also render the icon. So we
can use curly voice at render. We can
say icon.
So let's render one at 50 by 50 pixels,
100 by 100 pixels,
and 200 by 200 pixels. All right. So let
me also add a container.
All right. So now in the example, we can
increment count. As you can see, now
it's shared across the component
instances. And we can also reset the
count. And of course, we can also see
our beautiful icon being rendered at
different resolutions.
As you can see, the module script lock
is very useful. You can use it for
anything from controlling media playback
across component instances to just
exporting some utilities from the module
you want instead of creating a new file.
All right, friends. In this section,
we're going to learn how to create
delightful user interactions with swelt
transitions and animations. So let's
look at a basic transition. Let's add a
message element with two spans inside of
it. And we can say hello world.
All right. So to add a transition, we
can use the transition directive. So we
can say transition colon and we can use
one of many built-in swel transitions.
So let's say fade and let's import fade
from swel/transition.
And we can also pass some animation
options.
So we can see that we have multiple
options such as delay, duration, and
easing. And the options depend on the
type of the transition you're using. So
in this case, let's say duration and
this is going to be 600 milliseconds.
All right. So first I want to animate
this hello text during this 600
milliseconds and then I want to animate
this world text. So we can do the same
thing. We can say transition fade, but
instead of a duration, let's use a delay
and we can set it to 600 milliseconds.
All right. So, transitions only run when
the component is added to the DOM. So,
let's refresh the page.
And as you can see, the transition works
beautifully. All right. Let's make this
more interesting. So, let's create a new
piece of state called play. So, we can
say let play equals state. Let's say
false.
All right. So, let's create an interval
that's going to update the state of play
every 2 seconds. So let's say set
interval and then let's pass a call
back. So let's flip the value of play
and then let's say 2 seconds.
All right. So let's add an if block. We
can use the curly boys pound if
we can say play. So let's close the if
block
and that's it.
You can also define custom intro and
outro transitions. So for example,
instead of transition, we can use the
indirective and instead of fade, let's
import fly from swel/transition.
So now we have new properties to play
with. So for example, let's say x should
be -10. Let's leave the duration alone.
And now we can also specify an easing.
So has a bunch of custom easing that you
can pick from, but let's pick a fun one
like elastic out. So we can also import
that from swel/ easing and of course
let's specify an altra transition. So we
can say out fade. All right. So let's do
the same thing for the second span.
Instead of transition let's use the
indirective.
Going to say fly and x is going to be
10. And we can also use the same easing.
And let's specify the outro transition.
All right. Right. So let's do a bit of
formatting.
All right. So let's go to our example
and we can see that we have this fun
transition.
There is one important thing that you
should know about transitions and that
is local versus global transitions.
Let's say that we're looping over a
bunch of items that we also want to
stagger. So let's create an element
called stagger and inside of it I'm
going to use the each block. So let's
use a curly voice found each. All right.
So let's create an array of 50 items.
And let's also close the each block. All
right. Let's loop over the 50 items.
So we can say I + 1. So this should give
us 50 items. And it works. All right.
Let's add the transition.
So let's use the indirective and we can
import fade from swelt to create a
staggered effect. We can just use a
delay and we can multiply the index by
the time. So we can say 100
milliseconds. But this isn't going to
work. So if you go to our example, no
matter how many times we refresh the
page, nothing is going to happen. And
that is because transitions are local by
default. That means that transitions
only play when the block they belong to
is added or removed and not the parent
block like this each block for example.
To fix this you can use the global
modifier. So you can use this pipe and
you can say global. So now our example
should work if you refresh the page.
As you can see now we have a beautiful
staggered animation.
In past versions of Selt transitions
were global by default. So keep that in
mind if you ever encounter older SWEL
code.
All right. So we've seen that
transitions play when the element gets
added to the DOM. But if you're using a
framework like Cellit, that's not the
case since the initial payload is server
side rendered. So let's make a fade
component where we can control if the
transition should play. So let's open
the sidebar and inside of lib. Let's
create a new file. So let's create a
transitions folder. And then let's
create a new fade. component inside of
it.
All right. So let's start by creating a
script block. And now we want to
structure the children if the transition
should play and the transition options
from props.
And if you're using something like
socitate and you just want to
automatically play the transition when
the element is added, you can just use
an effect.
So you can just say play true when the
element is added. But in this case, we
want to manually control if we can play
the transition. All right. So, let's use
an if block. We can use the curly boys.
So, if play is true, then we're going to
play the transition. So, let's close the
if block. And inside of the if block,
we're going to create a div. All right.
So, we can use a transition. So, let's
import fate from swelt/transition.
And then we can pass the transition
options. So, it should be options.
So, inside of the div, we can render the
children. So you can use our curly boys
at render and then we can render the
children. Now let's go back to
app.swelt. All right. So now inside
appsel we can use the fade component. So
let's just import it. So we can say
something like boo and let's use a
spooky ghost.
And as you can see we have a warning
because these props are required. But we
can actually make them optional. So if
we go here let's say that we don't want
to play this transition by default. And
we can also use an empty object by
default or the transition options.
All right. So now we no longer get a
warning here. But of course we can
specify the transition options. So we
can say duration should be 2 seconds.
All right. So we can go to our example
and we can see regardless how many times
we refresh the page the transition isn't
going to play.
But we can of course specify play.
But this is redundant. We can just say
play. And let's refresh the page. And
you're going to see it still doesn't
work. And you probably already know why.
So if we go to the fake component, we
learn that transitions are local by
default. So we need to make this
transition global. So let's use the pipe
and say global. All right. So now our
transition should play when the outer
block gets removed or added. And if we
refresh the page now, you're going to
see we get a spooky transition.
All right, friends. Not only are we able
to use Swell's built-in transitions, we
can also specify our own custom
transitions. And a custom transition is
just a simple function that returns an
object with the transition options and a
CSS or tick function. All right, so
let's create a custom transition. So we
can start by creating a function named
custom transition. We get a reference to
the note and we can also pass the
transition options. All right, so let's
dstructure the options and we can set
some default values.
So here we can say the duration should
be 2 seconds.
There should be no delay and let's use a
custom easing. So we can say elastic
out. So we can import that from Sel. And
now we can return an object with the
transition options. All right. So now
let's return an object with the
transition options. So we can say
duration
delay and easing. All right. So this is
the important part. We can either return
a CSS function which is going to use CSS
key frames to generate this transition
or if you need to use JavaScript for
your transition, you can return a tick
function. So let's return a CSS
function. We can say CSS and we get the
T argument. So this is going to be the
progress of the transition that goes
from 0 to 1. And then we can return some
CSS. So let's use back tick.
So we can say color HSL. So let's use a
template literal. So we want to animate
the hue. So we can say 360 * t. And then
we can say 100% for the saturation and
80% for the lightness.
All right. So let's also add a
transform. So we can say transform scale
and let's also use a template literal
and then we can just say t. So we want
to animate the scale from 0 to one. All
right. So let me briefly explain what
this t means. So for example, let's say
that you have a transition where an item
goes from 0 pixels to 100 pixels over 2
seconds. So if t is a value that goes
from zero to one, that means that if t
is 0.5 for example, then the progress of
this transition is going to be at 50
pixels. And it's really that simple. And
besides t, we also have this u value
which is just the opposite. All right.
So, let's use our custom transition. So,
let's go here. Let's create a heading.
All right. So, we can use the custom
transition like any other transition.
So, we can use the indirective and we
can say custom transition.
All right. So, let's save this and go to
our example. And we can see it works
beautifully. How cool is this, friends?
All right, friends. Let me show you a
custom transition that uses JavaScript.
This is really useful when you want to
use JavaScript to control a transition
such as a typewriter effect or this
scrambling text effect. So I'm just
going to copy and paste this custom
transition here. So if I reload the
page, we can see how it works and it's a
really cool effect. All right, so let's
talk through it. So here we have some
hackerman characters and then we just
have a simple function that gets a
random character. So here is our custom
transition named scramble text. As
before, we get a reference to the node
and the options. So, we set some default
values here. But now, we're using
JavaScript. So, we get the final text
from the node text content. And we get
the amount of characters here. All
right. So, now we only have to return an
object with the transition options. But
now, instead of CSS, we're going to use
this stick function to use JavaScript.
So, we're going to accumulate the result
in this output variable. Then, we're
going to loop over the characters in
this for loop. So, we're saying if T is
higher than I divided by length, then
we're going to rest the character at its
final position. Otherwise, we're just
going to get a random character. And
then each time we're updating the nodes
text content. So, we can just assign it
to output. All right. So, now in the
rest of the code, we can just use this
like any regular transition. We're using
a monospace font, so the letters have
the same width. And then we can use our
custom transition by using the
indirective. And we can specify that it
should be the scramble text effect. And
that's it. Now we have this wonderful
scrambling text effect.
All right friends, let me show you
something that is going to blow your
mind and that is coordinating
transitions between different elements.
So if you're familiar with view
transitions, basically swelt has them
built in. So here I have an example that
I prepared for you. Here we have just a
simple example where we're looping over
these posts and we can archive them and
unarchive them. And the code is really
nothing special. Here we have these
random posts and we have these two
functions where we can archive and
remove a post. And in the template we
just have two sections. The first one
being post. So we're looping over the
post and we're filtering them based on
the ones that aren't archived. And then
we have another section which is the
archive.
So here we're doing the opposite. We're
just filtering the post based on the
ones that are archived. And that's
pretty much it. As I said, there's
nothing special going on here. All
right. So what is the problem? So in
this example, when we archive a post and
then if we unarchive it, we're going to
see that the item just magically
teleports. And in the real world, items
don't behave like that. And this is
really a worse user experience because
it's not clear what happened. To fix
this, we can use the crossfade
transition.
All right, so the only thing that we
have to do is import the crossfade
function from swelt/transition.
The crossfade function creates two
transitions named send and receive. So
we can destructure those
from crossfade. And we can just pass an
empty object for now. All right. So the
only thing that we have to do is go to
an each block here where we have
article.
So we can use the indirective
and use the receive transition which
accepts a unique key. So we can say key
and we can set it to the post. So swelt
knows what items to transition. All
right. So now we can use the out
directive. We can use the same
transition and we also need to pass a
key and we can say post. All right. Now
we have to do the same thing for the
archive section. So we can just copy
this code. Let's go to the archive.
So again here where we have this each
block and this article. We can just go
here.
We can copy and paste our code. So let's
save everything. And now we can go to
the example and we can see the magic
happen. So, let's archive a post. And as
you can see, it's no longer going to
teleport.
How magical is this, friends?
And we can also specify some crossfade
options.
As you can see, we can pass a delay,
duration, easing, and a fallback
transition. For example, we can base the
duration on the element distance. So,
let's use a duration function, and we
can get the distance. And now we can say
math square root.
And now we can multiply the distance by
200.
And you can also provide a fallback
transition if no match is found. So we
can say fall back. And we get a
reference to the node with the options.
So we can return a custom transition
object. We can use the CSS function. We
get access to team. We can use back.
So let's say transform
scale and then we can use template
literals. We can just pass T and we can
also animate the opacity
and that's it. All right. So now if we
go to the example we can see that the
duration is based on the distance. How
cool is that?
All right friends let's talk about flip
animations. Currently there's a problem
with our transition. So let me show you
that if we remove this fallback, let me
set a duration to something more obvious
like 2 seconds. So let's go to our
example. So when I press archive on this
post, you're going to see that we have
this empty space here. So when the
transition is done, the elements are
going to snap back in. And this looks
janky. So we can see it again. So let's
archive the second post. It's empty. And
then the items snap in. And we can see
the same thing if we unarchive the post.
All right. So to fix this, we can use
the flip function from swelt. So back in
our code, the only thing that we have to
do is import flip from
swelt /an animate. All right. So now
back in our template where we have the
each blocks.
So we can go here. We can use a new
directive called animate which only
works inside each blocks. and then we
can use flip. And of course, you can
also pass in some options here such as
delay, duration, and easing. But in this
case, we're going to ignore it. All
right. So now we have to do the same
thing for the second container.
So here we have article. We can say
animate flip. And that's it. All right.
So now if we go back to the example, we
can archive a post. And you're going to
see that the items are going to animate
gracefully.
How cool is that? Let's archive the
second post. We can also unarchive the
post.
And of course, this also works if you
delete a post.
And that is the power of flip
animations.
And flip animations don't depend on
crossfit transitions, but as you can
see, they work great together. All
right, friends. Just like with custom
transitions, you can also make custom
animations. All right, so let's look at
an example.
So here I have a simplified version of
the flip animation. As you can see, we
get a reference to the node. We get the
before and after state and we also have
some animation options. So in this case,
we're calculating the delta for X and Y,
and then we're calculating the scale
based on the width and height. And just
like with custom transitions, we can
return this animation options including
a CSS function. And then we can do some
math and we can apply a transform. And
here we have a simple shuffle function.
And we're creating 10 items, same as in
the GF flip example. In fact, we're
using the same markup from that example.
So here we're going to loop over a bunch
of items. As you can see, we can shuffle
the items, but I also want to animate
them. So going back to our code here
inside of the each block, we can use the
animate directive and we can just say
flip. So now if we go back to the
example, we can see that we can flip the
items.
Now that's magical.
And we can even log the value of the
before and after state.
So you can say console log.
So let's say from to. So now if we open
the developer tools
and let's shuffle the items, you're
going to see we get the before and after
state for every item. So now using the
animate directive and the flip function,
we can animate the difference in their
position.
Let's talk about tween values and
springs. Imagine if you could take the
CSS animation engine, but you can
interpolate any number including objects
and arrays. This is where swelt's tween
and spring classes come in. All right,
let's look at an example. So let's
create a value named size. And instead
of defining this with state, we can say
new. And if we start typing tween, let's
import tween from swelt/motion.
And if you're wondering, because you're
animating between two values, that is
what tween means. All right. So we can
pass the target value of 50. So this is
going to animate this value from 0 to
50. And then we can pass some animation
options. So we have a couple of options.
We can pass the delay, duration, easing,
and even a custom interpolation
function. So if you want, you can
interpolate between colors and anything
else. I recommend checking out the D3
interpolate library, which gives you a
bunch of interpolation functions out of
the box. All right, but in this case,
we're going to specify a duration 300
milliseconds. And I'm also going to use
an easing. So let's say cubic in out. So
we can import that from swelt/ easing.
And that's it. So now we can define two
functions. The first function is going
to be called on mouse down.
And tween has a couple of properties on
it. So we can look at size dot. We can
see we get current. So this is what we
can use in the template to get the
value. We can use a set method. So for
example, we can say 200. And then we can
override the animation options if you
want. And this is also a promise. So you
can await this if you want and make this
async. But in this case,
we can just say size dot target and we
can say 150. And this is the quickest
way to update the value. All right. So
let's copy over this function. So
instead of mouse down, this is going to
be mouse up. And we can use the default
value. All right. So let's create an SVG
with a circle.
So we can say the width and the height
are going to be 400.
And then we can specify the viewbox. And
you can think of a viewbox like a camera
for the SVG. So for example here we can
say the X and Y coordinates should be 0
0. And we can also specify what the
camera sees. So this is going to be 400x
400.
All right. So let's create a circle.
So let's pass the event handlers.
So we can say on mouse down and this is
going to be on mouse up. All right. So
let's specify the circle coordinates.
And for the radius we can say size
dotcurren.
And let's also add a fill color.
We can say orange red. And that's it.
And we can even disable this
accessibility warning. So we can go to
quick fix and we can just disable it.
All right. So let's go to the example.
And now when I hold the mouse down on
the circle is going to grow larger. But
when I release the mouse is going to go
back to the original position.
How cool is that? All right. So besides
the twin class, there is also a spring
class. So you can use spring physics
based on Hook's law. So here where we
define the tween instead of twin we can
say spring and we can also import spring
from swelt/motion.
So spring physics are based on hooks law
and they don't have a duration. Instead
you can specify stiffness, damping and
precision. So for example we can go here
and instead of using these values we can
say that stiffness should be 0,
damping 0.25 25 and precision should be
0.1. So we don't need this easing and
let's remove the tween.
All right. So now let's go to the
example.
So when I hold the mouse button down,
it's going to grow larger. And when I
release the mouse, it's going to go back
to the original position. As you can
see, it feels a lot more natural and
springy.
All right, friends. In this section,
we're going to learn how we can use
third-party JavaScript libraries in SWL.
So, if a specific SWL package isn't
available, you have the entire
JavaScript ecosystem at your fingertips.
Unfortunately, third party JavaScript
libraries usually require direct access
to the DOM and they don't understand
reactivity. So, in this example, we're
going to use the popular GP animation
library in Swelt. All right. So, let's
say that you've read the GE
documentation and you want to try out
their example. So we can create this box
element and then we can just simply
create an animation. Let's import gap
and then we can use this two method to
create a twin animation and then we can
specify the target which is going to be
the box class and we can specify some
animation options. So we can say
rotation 360°
the X position should animate 200 pixels
and the duration should be 2 seconds.
All right. So let's go to our example.
But you're going to see that nothing
happens. All right. So let's open the
developer tools for any clues. So we can
see the warning from gap target.box not
found.
And this is because when this line of
code runs, box doesn't exist in the DOM
yet. So we can use the onmount life
cycle function.
So if we say onmount, we can import on
mount from swelt. And let's pass a
callback function.
and we can place our animation inside of
it. And let's also organize the imports.
So, let's go back to the example. And
when I reload the page, the animation
should play.
Awesome. In this example, we're saying
dotbox, which is just a query selector
under the hood, but of course, we can
pass a reference to the element instead.
So, let's create a target variable,
and then we can bind the value of this
element using this
And then let's bind it to the target.
All right. So instead of saying dotbox,
we can just pass the target.
And we can also say const
tween. So we can return a cleanup
function. So we can go here and we can
say return.
And we can say tween.kill.
And of course if we reload the page,
everything works the same as before. And
of course, instead of using onmount, you
can use an effect. So, we can remove the
onmount import. And instead of on mount,
we can just use an effect. And this
works the same. But keep in mind that
effects aren't life cycle functions. An
effect life cycle depends on the value
inside of them updating. So, in this
case, if you make target reactive and
update it at any point, then this effect
is going to rerun. There is no right or
wrong way here. If you don't care about
tracking values, then you can just use
onmount. The more important thing is
that you don't treat effects like life
cycle functions. So if you understand
how effects work, then you're not going
to run into problems. All right, we got
this gap example working. But now let's
look at how we can bend it to our will
and create a declarative component out
of it.
So let's take this code and then let's
open the sidebar and inside of lib I'm
going to create a new component called
tween.swel.
All right. So in our new component let's
paste the code. Then let's go to the
top. I don't want target to be reactive.
So I'm going to remove this. All right.
So for the props we want to the
structure tween which is going to be a
reference to this animation so we can
control it. And we're going to make this
bindable. And then we want the animation
options. I'm going to name this vase
because that's what GUB calls them. So
for example, if we go here to two, you
can see GE has targets and varss. I
don't know why it names its but it is
what it is. And of course, we want to
accept the children because we want to
be able to animate anything.
All right. So now instead of defining
twin here, we can just reassign it and
then we can remove this part and we can
use our animation options. And the last
thing that we have to do is render the
children.
So we can use the curly boys at render
children.
And that's it. All right. So now let's
go back to app.swelt and let's use the
tween component.
So we can import tween from
lib/ween.swelt.
And now we can get a reference to the
animation by binding tween
to animation. And we can create it here.
All right. So, let's pass the animation
options. So, we can say VS
rotation 3060,
X should be 200 and duration should be 2
seconds. All right. So, we can animate
anything that we want. So, let's create
a button with a box class. We can say
play. And because we have a reference to
this animation,
we can say on click
and we can say animation restart. And
this is just a built-in gap method. All
right. So let's do a bit of cleanup.
And inside of tween, we can also remove
this box class.
And that's it. So now when you go to the
example, we can press play and the
animation is going to play.
So far, we learned that we can use
onmount to get a reference to an
element. This can be a bit tedious and
boilerplate tip when you want a quick
reference to an element. So, what if
instead of component level life cycle
functions, you had element level life
cycle functions. In that case, you would
have attachments. Attachments are just
regular functions that you can attach to
regular elements that run when the
element is added to the DOM or when
state inside of them updates. So let's
look at the same gap example using
attachments. All right. So I'm going to
start with the box and then let's format
this a bit.
So we can say curly boys at and the
attach keyword. So now we get our
reference to the node element but you
can name this however you want. So let's
name this box and then let's create a
function. So let's import gap
and now we can say dot2. So now we can
pass the target and the animation
options. So let's say rotation 360 x is
going to be 200 and the duration is
going to be 2 seconds. All right. So now
we can look at our example. So now if I
reload the page, the animation should
work. As you can see, this is a lot more
convenient. All right. How do we create
a tween function that we can reuse
across components similar to the tween
component that we created earlier? All
right. So the first thing that we have
to do is create a tween function which
accepts the animation options and an
optional ref callback. So we get a
reference to the tween.
So inside of the function we can say let
tween and because attachments are just
regular functions we can just return an
attachment. So let's return an
attachment.
And of course here we get our reference
to the target element. And inside the
function we can reassign tween to be gap
2 to with the target and animation
options.
And if you need a reference to the
animation, we can invoke the call back.
And let's also return a cleanup.
That's it. So now in the markup,
instead of using an inline attachment,
we can use the attachment that we made.
So let's use the curly voice at attach.
So let's use the tween attachment that
we created.
So let's pass the animation options.
So we can say rotation 300 is 60, x is
going to be 200 and the duration is
going to be 2 seconds.
And we can use the optional callback to
get a reference to the animation. So we
can get a reference to the tween. And
now we can assign it to animation.
So let's create animation.
All right, friends. This is the coolest
part. So now instead of div, we can turn
this into a button.
And let's say play inside of it.
And now we can say on click
animation.
All right. So now if we go to the
example and if we press play, we can see
that our animation works. How cool is
this?
All right, let me show you another cool
thing about swelt and that is special
elements. So swelt has special elements
you can use at the top level of your
component. Let's say that we want to
listen to the window scroll position. So
let's create a script block and then we
can say let scroll y equals state and
let's say zero. All right. So now we
need a handle scroll function.
So we can update scroll Y to equal
window scroll Y. All right. So let's use
onmount.
So we can add an event listener to the
window. We can say window add event
listener. Let's listen to scroll. And
then we can invoke our handle scroll
function. And let's return a cleanup
function. So we can say window remove
event listener. We want to remove the
scroll event. And we need to pass our
handle scroll function. All right. So
now let's create a div element named
scroll. And then we can say scroll y and
we can say pixels. And that should be
it. This is what it would usually take
to listen to an event and update the
value. So now if we go to the example
and if we scroll, we're going to see
that the scroll position is going to be
updated.
Thankfully there's a better way to do
this in swelt. So instead of using
onmount where we also have to do the
cleanup we can use a special element
instead. So let's remove onmount
and then at the top level of the
component we can use swelt colon window.
So we can say on scroll and we can use
our handle scroll function and swelt
also takes care of the cleanup. So now
if you go to our example it works the
same as before.
We can make this even simpler. Instead
of using events, we can use bindings for
properties like the scroll position. So
in this case, we don't even need the
handle scroll function
and we can just remove this. So the only
thing that we have to do is bind the
value. So we can bind scroll Y. And now
if we go to the example, it works the
same as before.
Believe it or not, we can make this even
simpler. Swelt also exports reactive
window values from reactivity/ window.
So you don't even have to use a special
element. So we can remove this. And now
in the script block, we can import
scroll y from swelt/reactivity/
window. And now in the template, the
only thing that we have to change is to
say scrolly do.curren. And that's it.
Now our example should work the same as
before.
Awesome.
All right, friends. Let's talk about
Soel's reactive data structures and
utilities. All right, so here I have a
simple PokƩmon search example. So here
we have a search query which is called
name and then we're storing PokƩmon in
this map cache. So here inside of this
get PokƩmon function, we can just check
if the PokƩmon exists inside of the map.
If that's the case, we can just return
early. Otherwise, we're going to fetch
the details for the Pokemon. And then
when we get the data, we can just add
the Pokemon. And of course, we're using
an effect to get the PokƩmon. And inside
our template, we're just binding the
value to the name. And then here, we're
looping over the PokƩmon. Since it's a
map, we can just structure the name and
the details. And then here, we're going
to use the details element with a
summary. And then we can just stringify
the details. And that's it.
And we also have a button to clear the
map. All right, so trick question. Is
this going to work? As you can see,
nothing is showing in the example. All
right, so how do we fix this? Well,
maybe we can pass this map to state. I
don't know. Let's try it.
All right, at least the example is
showing. But if you search for something
like Pikachu, nothing works. And that is
because of course this still isn't
reactive.
because there is no way for sale to know
that this method is going to be
reactive. But thankfully, swelt has
reactive versions of some built-in
JavaScript objects like set map and so
on.
So let's go here where we define the map
and instead of using set or new map, we
can say new swelt map and we can import
this from swelt/reactivity.
All right. So now if you go to the
example, let's search for Pikachu. And
you can see it's reactive. So we can see
all the details for Pikachu.
All right, let's search for Charizard.
And you're going to see now we have
Charizard details.
And we can also clear the PokƩmon.
Awesome. You can find even more reactive
built-ins in the Swell documentation. So
here you have a media query class for
example if that's something that you
need. We have swel date so you have
reactive dates. Here you have swel map
which we just used. You have swel set
which is really great. You have swel for
working with urls.
Then there's also swel search params
and so on. You can read more in the
swell documentation. So you can use any
of these in your project. All right
friends. This is a more advanced topic,
but I think it's useful to know whenever
you're trying to make an external
event-based system reactive in swelt.
And an external event is any event you
can subscribe to and listen for changes.
In this example, we're going to create a
gap timeline. And our goal is going to
be to synchronize the timeline with an
input. So, let's start with a basic
timeline. In this example, I have a
simple class called timeline. And here I
have a reference to the GSAP timeline.
When I create this timeline, I'm going
to invoke this method create timeline.
So, this is going to create the timeline
when the component mounts. And this is
just so we have a nicer API when we
create this timeline. So, we can just
pass in an array of arrays for the
animation key frames
and then we add it to the gap timeline.
And in the template, we just have two
items, box one and box two, and an
input. So we can go to the example and
when I reload the page you're going to
see that we have a simple animation
here. Our goal is going to be to
synchronize this input with the gap
timeline.
All right. So let's start with a naive
approach here at the top where we define
our class below timeline. We can specify
a new reactive variable called time. So
this is just going to be state. And then
inside of the constructor we can use an
effect to update it.
So let's create an effect provide a
callback. So we need to reference the
gap timeline. So we can say this
timeline and we can invoke the seek
method. So we can update the timeline
playhead each time this time updates.
Now we just need to create the getter
and setter. So we can bind the value
and we can say get time.
So we can return time and then let's
create a setter. So we can say set
time the new value that we're going to
receive and then let's update time to
the new value. So now in our template we
can bind this value to the input. So we
can say bind value
TL for timeline and then we can pick
time. All right. So let's save and
format the document. So, if we go to our
example, we can reload the page to see
our animation and we can now control the
timeline, but you're going to see that
it's still not synchronized. So, if I
let go, you're going to see nothing is
going to happen. All right. So, how do
we synchronize the timeline? And this is
where using events comes in. So, we can
subscribe to Gap's on update event and
then we can update time which is going
to trigger the effect. So, we can
synchronize the input with the GSAP
timeline. All right, back in our code.
Let's go to the effect. So now we can go
here.
Let's reference the timeline and then we
can use gap's event call back API. So we
can subscribe to on update and then we
can pass a call back function. All
right. So each time the animation
updates, we're going to update the time.
So we can say this time and now we need
to reference the timeline. So you can
say this timeline and we need to get the
latest time by using gap's time
function. Now when this updates is going
to trigger this effect which is going to
synchronize the timeline and return the
latest value of time. Now in our example
we can change the input and you're going
to see it's synchronized with the
timeline.
And that's awesome. But there's a
simpler way. All right. Let me show you
a simpler way how to make reactive
events using the create subscriber
function. So we can delete this effect
and this code below it. And then we can
say create subscriber. So let's import
that from swelt. As you can see it's
imported from swelt/reactivity.
And we can pass a callback function to
create subscriber where we get this
update function.
This is going to let us create
subscribers.
So we can just say this timeline and we
can say event call back we can subscribe
to on update but in this case we can
just pass the update function and we can
also return a cleanup function. So let's
say return
this timeline
event call back
on update and we can say null. Of
course, this way of doing cleanups is
specific to GSAP, but the method remains
the same for any event that you can
subscribe to. All right. So now, instead
of having this extra variable time, we
can just create a subscriber. We can
just say subscribe and then we can store
the reference to the function that's
returned from create subscriber. So we
can say this subscribe equals to create
subscriber.
Now we can subscribe to the timeline
updating.
So we can go to our getter and this
makes things a lot simpler. The only
thing that you have to do is say this
dossubscribe.
So this makes the time value reactive
when it's read inside of an effect. The
best part is that you don't need any
extra state because now you can just
return the value from the timeline. So
we can say timeline dot time and of
course we can update the timeline. So we
can go here and we can say this dot
timeline seek and we can pass the new
value. But of course don't forget to
invoke the subscribe method. So let's
see if the example works. So if I reload
the page the animation should play. And
as you can see the input is synchronized
with the timeline.
As you can see, using create subscriber
can make your code a lot simpler. And
not only that, but you can control when
you want to run this update method. The
create subscriber API is great whenever
you're trying to make an external
event-based system reactive in SEL. That
can be anything from browser APIs to
random JavaScript libraries. you can
subscribe to and listen for changes.
All right, friends, without a doubt, at
some point you're going to run into
legacy Swelt code. So, Swelli was a
large shift from previous versions of
Swelt that introduced a new system of
reactivity with runes and snippets
replacing slots among other things. So,
if you ever need a quick overview, you
can find the legacy API section in the
Swelt documentation. And there are even
other things that aren't listed here
such as swelt stores. In the past,
stores were required to have reactive
state outside swel components. The
reason I didn't cover stores in this
course is because I don't think that you
no longer need them because now you have
universal reactivity with runes. But if
you ever run into stores, then you can
read this section in the swell
documentation.
There is one more thing worth mentioning
when it comes to legacy swell. By
default, cell components are in legacy
mode unless you use runes in your
component. And this is worth noting
because you might run into unexpected
behavior when you're using legacy
components. So there's two ways that you
can ensure that your component is in
runes mode. The first method is just
using runes. So you can say let count
equal state and now your component is
automatically in runes mode. The other
method is using this special element to
specify some compiler options for swelt.
So we can say swelt colon options
and we can say runes equals true. You
can also specify the swel compiler
options for the entire project. And this
is what I've done for this entire course
so we don't run into any problems. So if
we go into the swell config you can see
under the compiler options I enabled
runes by default. And in the future
versions of Selt, this is going to be
the default.
All right, friends. In the last section,
I want to talk about using Selt with AI.
To be completely honest, I'm not a big
AI user, but I know that this is
important to a lot of people. Newer AI
models seem to be getting better at
supporting the latest SW syntax, but
it's still not perfect, and it's often
going to hallucinate features that don't
exist with overwhelming confidence. So,
if you're using AI and want the latest
SWEL syntax suggestions, SWEL has LLM
friendly documentation you can feed to
an AI context window for more accurate
suggestions. All right, friends, that's
it. I hope that you enjoyed this felt
course and I'm going to catch you in the
next one. Peace.
Thank you Sentry for sponsorsing! Check out Sentry at https://sentry.io/. š“ Patreon: https://www.patreon.com/joyofcode š¬ Discord: https://discord.com/invite/k6ZpwAKwwZ āļø Links š Starter: https://github.com/joysofcode/learn-svelte š Post: https://joyofcode.xyz/learn-svelte š Timestamps 0:00 Intro 0:35 Sponsor 2:02 Requirements 2:34 Project Setup 4:22 What Is Svelte? 6:46 Single File Components 8:32 Dynamic Attributes 11:42 Component Styles 11:53 The Style Directive 13:40 Scoped Styles 14:21 Global Styles 18:02 Dynamic Classes 21:56 Reactive State 23:26 Deeply Reactive State 24:55 $state.raw 25:44 $state.snapshot 27:15 Destructuring State 28:10 Derived State 29:41 Deriveds Update When They Change 31:03 Derived Dependency Tracking 32:32 $derived.by 34:36 Effects 36:54 Values Read Asynchronously Are Not Tracked 39:11 Untrack 40:53 Effect Dependency Tracking 42:52 $inspect 43:15 When Not To Use Effects 45:44 Using onMount Instead Of Effects 47:02 When To Use Effects 50:20 $effect.pre 53:47 State In Functions And Classes 54:52 Reactive Properties 56:17 Reactive Containers 58:58 Universal Reactivity 1:00:09 Passing State Into Functions And Classes 1:02:25 Reactive Global State 1:04:16 This Keyword 1:04:45 Why You Should Avoid Effects 1:07:40 $effect.root 1:08:45 $effect.tracking 1:08:57 Effect Orphans And Root Effects 1:11:38 Side Effects In Event Handlers 1:12:29 How Svelte Reactivity Works 1:19:20 Using Template Logic 1:19:32 Conditionals Rendering 1:21:51 Looping Over Data 1:28:10 Working With Asychronous Data 1:30:26 Asynchronous Svelte 1:31:16 Recreating Elements 1:32:33 Template Variables Using Local Constants 1:34:45 Listening To Events 1:40:16 Using Data Bindings 1:41:07 Two-Way Data Binding 1:43:45 Function Bindings 1:46:46 Media Bindings 1:48:08 Readonly Bindings 1:48:57 Svelte Components 2:11:38 Component Composition 2:11:56 Component Nesting 2:17:25 Snippets 2:19:55 The Context API 2:23:01 Module Context 2:27:22 Transitions And Animations 2:27:30 Transitions 2:30:32 Local And Global Transitions 2:32:13 Autoplaying Transitions 2:34:59 Custom Transitions 2:35:07 CSS Custom Transitions 2:37:41 JavaScript Custom Transitions 2:39:21 Coordinating Transitions Between Different Elements 2:43:10 FLIP Animations 2:45:11 Custom Animation Functions 2:46:46 Tweened Values And Springs 2:51:09 Using Third Party Libraries 2:51:21 Component Lifecycle Functions 2:53:32 Effects Versus Lifecycle Functions 2:54:14 Creating Components From Third Party Libraries 2:56:42 Element Lifecycle Functions Using Attachments 3:00:17 Special Elements 3:02:09 Window Bindings 3:02:36 Reactive Window Values 3:03:14 Reactive Data Structures 3:05:47 Reactive Events 3:12:04 Legacy Svelte 3:13:52 Using Svelte With AI 3:14:24 The End #joyofcode #svelte #course