State Management
Manage temporary data using States, with persistence and namespacing options.
States provide fast, synchronous, in-memory storage for your Robo.js project. They hold data for the duration of the current session and clear when the process shuts down.
States do survive hot reloads and /dev restart commands during development, so you will not lose session data while iterating.
Usage
Set and retrieve state values with setState and getState:
import { setState, getState } from 'robo.js'
export default () => {
let currentPoints = getState('currentPoints') ?? 0
setState('currentPoints', currentPoints + 10)
return `You've gained 10 points! Your current total is ${currentPoints + 10} points.`
}import { setState, getState } from 'robo.js'
export default () => {
let currentPoints = getState('currentPoints') ?? 0
setState('currentPoints', currentPoints + 10)
return `You've gained 10 points! Your current total is ${currentPoints + 10} points.`
}Retrieve state values in another command:
import { getState } from 'robo.js'
export default () => {
let currentPoints = getState('currentPoints') ?? 0
return `You currently have ${currentPoints} points.`
}import { getState } from 'robo.js'
export default () => {
let currentPoints = getState('currentPoints') ?? 0
return `You currently have ${currentPoints} points.`
}Forking Namespace
When multiple modules use the same state key name, their values can collide. For example, two modules both using a counter state would overwrite each other.
The problem:
import { setState, getState } from 'robo.js'
export default () => {
let counter = getState('counter') ?? 0
setState('counter', counter + 1)
return `Counter in Foo module is now ${counter + 1}.`
}import { setState, getState } from 'robo.js'
export default () => {
let counter = getState('counter') ?? 0
setState('counter', counter + 1)
return `Counter in Foo module is now ${counter + 1}.`
}import { setState, getState } from 'robo.js'
export default () => {
let counter = getState('counter') ?? 0
setState('counter', counter + 5)
return `Counter in Bar module is now ${counter + 5}.`
}import { setState, getState } from 'robo.js'
export default () => {
let counter = getState('counter') ?? 0
setState('counter', counter + 5)
return `Counter in Bar module is now ${counter + 5}.`
}To prevent this, fork the state into a separate namespace:
import { State } from 'robo.js'
const { getState, setState } = State.fork('foo')
export { getState, setState }import { State } from 'robo.js'
const { getState, setState } = State.fork('foo')
export { getState, setState }Then import the forked functions in your module's commands:
import { setState, getState } from '../state.js'
export default () => {
let counter = getState('counter') ?? 0
setState('counter', counter + 1)
return `Counter in Foo module is now ${counter + 1}.`
}import { setState, getState } from '../state.js'
export default () => {
let counter = getState('counter') ?? 0
setState('counter', counter + 1)
return `Counter in Foo module is now ${counter + 1}.`
}The foo module now has its own isolated namespace, preventing conflicts with other modules.
Persisting Data
By default, states clear when the process exits. To preserve a value across shutdowns, use the persist option:
import { setState } from 'robo.js'
export default () => {
setState('importantData', 'Never forget this', { persist: true })
return 'Data has been saved and will survive restarts.'
}import { setState } from 'robo.js'
export default () => {
setState('importantData', 'Never forget this', { persist: true })
return 'Data has been saved and will survive restarts.'
}Under the hood, persisted state values are stored using Flashcore, so they follow the same storage rules.
Note: Persisted values must be serializable. Complex objects like Discord.js Guild or Message instances cannot be persisted.
Namespaces
Namespaces prevent key collisions when storing data for different contexts such as different servers or users.
Include a namespace in the options object:
setState('my-key', 'example-value', {
namespace: 'my-namespace'
})setState('my-key', 'example-value', {
namespace: 'my-namespace'
})Retrieve values using the same namespace:
const value = getState('my-key', {
namespace: 'my-namespace'
})const value = getState('my-key', {
namespace: 'my-namespace'
})Practical Example
Track per-server participation in a daily challenge:
import { getState, setState } from 'robo.js'
import type { ChatInputCommandInteraction } from 'discord.js'
export default (interaction: ChatInputCommandInteraction) => {
const userId = interaction.user.id
const challengeId = 'daily-challenge'
const userParticipation =
getState(userId, {
namespace: interaction.guildId
}) ?? {}
const newParticipationCount = (userParticipation[challengeId] ?? 0) + 1
userParticipation[challengeId] = newParticipationCount
setState(userId, userParticipation, {
namespace: interaction.guildId
})
return `You've participated in '${challengeId}' ${newParticipationCount} times in this server.`
}import { getState, setState } from 'robo.js'
export default (interaction) => {
const userId = interaction.user.id
const challengeId = 'daily-challenge'
const userParticipation =
getState(userId, {
namespace: interaction.guildId
}) ?? {}
const newParticipationCount = (userParticipation[challengeId] ?? 0) + 1
userParticipation[challengeId] = newParticipationCount
setState(userId, userParticipation, {
namespace: interaction.guildId
})
return `You've participated in '${challengeId}' ${newParticipationCount} times in this server.`
}Using the server ID as the namespace ensures that participation counts are tracked independently per server.
State vs. Flashcore
Use State for data that is needed temporarily or within a single session. Use Flashcore when data must survive restarts.
- Speed: State operations are synchronous and immediate. Flashcore is asynchronous.
- Lifetime: State clears on shutdown (unless persisted). Flashcore persists permanently.
- Storage: State uses in-memory RAM. Flashcore uses the filesystem or an external database.
- Data types: State can hold any JavaScript value, including class instances. Flashcore requires serializable data.
