DEV Community

Cover image for Here's a minimalist no-frills Redux Toolkit & LitElement example
Daniel Vivar
Daniel Vivar

Posted on

Here's a minimalist no-frills Redux Toolkit & LitElement example

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);
Enter fullscreen mode Exit fullscreen mode

<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)
Enter fullscreen mode Exit fullscreen mode

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 }
Enter fullscreen mode Exit fullscreen mode

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;
  }
...
Enter fullscreen mode Exit fullscreen mode

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())
  }
...
Enter fullscreen mode Exit fullscreen mode

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'
Enter fullscreen mode Exit fullscreen mode

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)