Picture by @16pxl
Tired of full boilerplate Redux examples? What about all the Redux Toolkit examples that try to explain how much complexity is now reduced into opinionated functions... based on your knowledge of Redux?
I believe it's the most minimalist examples based on the simplest systems which tell you all.
The way to build a complex system that works is to build it from very simple systems that work.
โ Kevin Kelly
Here I present you a simple system of a Redux Toolkit store and two LitElement based Web Components.
The Components
Our system is based on 2 components: one to control a variable and another one to show the state of the variable. As simple as it can get.
Of course you won't use Redux Toolkit to do that if you have just 2 components, but that's not why you're here, isn't it?
I've called them auth-control
and auth-info
, it's a dumb system to sign in and sign out just by clicking on buttons that say "Sign in" and "Sign out". See? No frills as promised ๐
<auth-info>
This component just shows a salute if you're signed in, based on an isAuthenticated
property.
import { LitElement, html } from 'lit-element'
class AuthInfo extends LitElement {
static get properties() {
return {
isAuthenticated: Boolean,
}
}
render() {
if (this.isAuthenticated) {
return html`
<p>Hi, you're signed in</p>
`
}
}
}
window.customElements.define('auth-info', AuthInfo);
<auth-control>
Here's the controlling component. As you can see, they have a property with the same name: isAuthenticated
.
import { LitElement, html } from 'lit-element'
class AuthControl extends LitElement {
static get properties() {
return {
isAuthenticated: Boolean
};
}
handleSignIn () {
this.isAuthenticated = true
}
handleSignOut () {
this.isAuthenticated = false
}
render() {
if (this.isAuthenticated) {
return html`
<button @click=${this.handleSignOut}>Sign Out</button>
`
}
return html`
<button @click=${this.handleSignIn}>Sign In</button>
`
}
}
window.customElements.define('auth-control', AuthControl)
The Store
The goal is to share the state of the property isAuthenticated
from a global state store. We will use Redux Toolkit to accomplish that:
import { createSlice, configureStore } from '@reduxjs/toolkit'
const { actions: authActions, reducer: authReducer } = createSlice({
name: 'isAuthenticated',
initialState: false,
reducers: {
signIn: () => true,
signOut: () => false
}
})
const { signIn, signOut } = authActions
const store = configureStore({
reducer: {
isAuthenticated: authReducer
}
})
export { store, signIn, signOut }
Note the neat use of destructuring to obtain all the necessary information from createSlice
. If you want to add more data to the store: just create a new slice, add the obtained reducer to the list of reducers to feed to configureStore
and export the actions so that your app can update the state.
The connection
Now we need to connect the store with both components. In order to do that we'll use a connect
mixin from Polymer project pwa-helpers.
This mixin will add logic to our component that will end up calling stateChanged
every time the state changes, so to complete the connection we have to implement this callback function as well.
Connecting <auth-info>
...
import { connect } from 'pwa-helpers'
import { store } from './state.js'
class AuthInfo extends connect(store)(LitElement) {
...
stateChanged(state) {
this.isAuthenticated = state.isAuthenticated;
}
...
That's it. You don't even need a constructor, as the store dispatches the initial state on connection.
Connecting <auth-control>
...
import { connect } from 'pwa-helpers'
import { store, signIn, signOut } from './state.js'
class AuthControl extends connect(store)(LitElement) {
...
stateChanged(state) {
this.isAuthenticated = state.isAuthenticated;
}
handleSignIn () {
store.dispatch(signIn())
}
handleSignOut () {
store.dispatch(signOut())
}
...
Same here, plus we imported the actions to dispatch when handling the events for the sign in and sign out buttons.
Final touches
An HTML page to hold the components together and a build process to resolve the dependencies for LitElement and others is all you might need. We're using Snowpack for that.
Buildless anyone?
You could perfectly use packages from a CDN like Skypack or UNPKG and skip the build process altogether if you fancy. Like so:
-- import { LitElement, html } from 'lit-element'
++ import { LitElement, html } from 'https://cdn.skypack.dev/lit-element'
Having a well thought buildless process have great benefits. In fact, Snowpack has an option to do just something like we just did there, using CDN packages instead of installing them manually. It's called Streaming Imports in case you're interested ๐.
Complete example
Please dig in the whole example I've done for you in Glitch. Feel free to remix it there and leave feedback in the comments ๐ Any question or comment will be welcome!
Top comments (0)