5. State
The next thing we should look at is the state
.
We have defined it previously as "A JavaScript object containing all the state exposed by your package". For example:
As you can see here, this theme needs some settings like the menu
or settings to define if it should show featured images or not, and then some state that is useful while the app is running, like isMenuOpen
.
You can access the state in the client console with:
State is a proxy, so you can see the original object clicking on [[Target]]
:
Why not separate settings and state?
First, here at Frontity we think the less concepts the better. Second, imagine a notifications
package wants to add an item to the menu
only when the browser actually supports notifications. That's super easy to do by just using the state
:
As you can see, packages can access the state exposed by other packages.
Finally, what if you decide that the app should be run with the menu open by default? Then you'd only have to set isMenuOpen
to true
in your frontity.settings.js
file. Yes, I know, that makes no sense, but I hope it gives you a sense of how flexible this pattern is.
Another good example of state
is tiny-router
. It exposes three props:
Here link
represents the current URL of your app and it changes when you use the action actions.router.set("/other-url")
in your theme.
If we were to create an analytics package, we could use state.router.link
when sending pageviews:
Finally, tiny-router
exposes a third prop called autoFetch
. This is a setting and, by default, is true
. If it's active, it fetches the data you need each time you navigate to a new route using: actions.router.set(link)
.
Here the most common scenario is that you will use your frontity.settings.js
file to set autoFetch
to false
when you want to control the fetching yourself:
These are the most important things you need to know about the Frontity state:
1. State should be serializable
Only objects, arrays and primitives (strings, numbers...) are allowed in the state
because it must be serializable. No circular dependencies are allowed either. The best way to think about it is: it's a JSON.
Actually, it is converted to a JSON when it's sent to the client. We'll talk later about how server-side Rendering works, but it is something like this:
First, this is what Frontity does in the server:
It gets the settings of the current site from
frontity.settings.js
.It merges the state exposed by each package with the state from
frontity.settings.js
.It gives each package the opportunity to populate
state
with an asyncbeforeSSR
action. SSR stands for server-side Rendering. This is usually used to fetch content from the WP REST API.It renders React using that initial state.
It sends both the HTML generated by React and the initial state to the client.
The client browser paints the HTML received from the server. Then, this is what Frontity does once the JavaScript is run:
It loads the
state
in the client using the initial state received from the server. This guarantees that when we render React again we will be in the very same place where we left on the server.It renders React again. It should produce the very same HTML we've sent from the server.
It gives each package the opportunity to run code with an
afterCSR
action. CSR stands for client-side Rendering.
2. All the state is merged together
As we've seen in the previous point, the states from frontity.settings.js
and your packages are merged together.
Let's imagine we have this setting file:
First, the states from my-awesome-theme
, tiny-router
and wp-source
get merged:
Then, the state from frontity.settings.js
file gets merged:
Then Frontity executes beforeSSR
to give each package the opportunity to modify the state. For example, the theme could use it to fetch content from the REST API:
This populates source
with some data. For example, if the URL is /my-post
:
Now everything is ready for the React render in the server!
3. State should be minimal
There are two reasons for this:
The initial state is sent to the client, so the smaller the better.
It's easier to cause out-of-sync bugs when the state exists in two different places.
For that reason, Frontity supports derived state.
Remember I told you that state
must be serializable and cannot contain functions? Well, that's still technically true, but you can include derived state functions. Let's take a look at an example:
Here we have a totalCount
field that represents the sum of all the shares we have in our posts. It looks great, but what happens if we update the shares of our second post?
Wouldn't it be much easier if totalCount
could be calculated reactively each time their dependencies change? That's precisely what derived state is for:
That's it! Now when you use state.share.totalCount
in React everything will be updated without having to do anything additional on your end.
You can also use derived state with additional custom parameters. Such a function works like a "getter" for a piece of state:
And then consumed like this: state.share.totalCountByRoute("/my-first-post")
, so you should be able to create derived state for pretty much anything.
Additionally, Frontity gives you access to both state
as well as [libraries
]('./libraries) in your derived state:
These derived state functions are stripped out from the initial state we send to the client but don't worry, they are reinstantiated later in the client by Frontity to ensure everything is back to normal :)
If you still have any questions about State in Frontity, please check out the community forum, which is packed full of answers and solutions to all sorts of Frontity questions. If you don't find what you're looking for, feel free to start a new post.
Last updated