Syntax Introduction: A Simple Counter
To understand the structure and syntax of using defineState to build a state manager, let's start with a simple but functional example. This example implements a counter state manager. It allows you to create the counter, increment the counter, and get the current counter value.
A state manager like counterManager is a function that creates instances of the state manager. Arguments you pass to the state manager function are available to the state manager instance as parameters when it initializes. Each state manager instance has its own set of values. Use values and actions from the state manager instance in your component template and JavaScript.
For example, to use the counterManager state manager in a component, save the result of calling it. Then access state manager values and actions through the saved reference.
Even this simple example uses some advanced JavaScript features. Let's break down the syntax a little bit at a time, to try to clarify the specifics.
-
defineState((...) => { ... }): ThedefineStatefunction takes a single argument: a callback function. The callback function is the{ ... }part, which we expand on in a moment. -
({ atom, computed, setAtom }, initialValue = 0): The callback function defines the state manager. The function is your state manager. The function can accept an arbitrary number of parameters, which you can use for setting the initial state.- The first argument passed to the function is an object containing the state primitives:
atom,computed, andsetAtom. Your state manager uses these primitives to define and change the values that it exposes. - After that object, your state manager can accept one or more parameters — here
initialValue— to configure the state manager instance. You can define parameters and default values as you would for any other JavaScript function.
- The first argument passed to the function is an object containing the state primitives:
-
const count = atom(initialValue): Inside the state manager, define your state. Here,atom()creates a reactive variable namedcount. The value ofcountis the only state managed in this example. -
const increment = () => { ... }: This is an action, which will be part of this state manager's public interface. It's just a function that encapsulates the logic for changing the state. Actions are the only way consumers of your state manager can change its internal state. -
setAtom(count, ...): Within theincrementaction, and anywhere else you need to modify state, use thesetAtomprimitive. This ensures that, when your state changes, LWC's reactivity system is notified of that change. -
return { count, increment }: Your state manager completes when it returns an object that defines the public API for your state manager instance. Components can access<instance>.value.countto read the state and<instance>.value.increment()to call the action.
The atom() function creates a reactive "atomic" value, which is essentially a wrapper around a piece of data that LWC can use to make components and other state managers reactive to changes in that data. An atom can be a simple value, like a number, or it can be a more complex value, like an object or array.
You can wrap any JavaScript value, but it's recommended to stick to JSON-serializable values or undefined for best compatibility.
If this is your first time using JavaScript with this structure, read through it a few times. It's not easy to understand the first time, or even the second.
To cement your understanding, start with this example and modify it, perhaps to add a second simple action, like double. Take it even further, and turn it into a game leveling manager. Your state manager should track and increment a second atom, currentLevel when certain incremental thresholds of count are reached. Allow the threshold to be set from an argument provided when an instance is created, and reset count whenever you level up. Give it a go, and see how much you learn!