Skip to content

Depot - A powerful and flexible state management library for modern applications.

Notifications You must be signed in to change notification settings

AndreRojasMartinsson/rbxts-depot

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

26 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Depot

Introducing "Depot" - a powerful and flexible state management library for modern applications.

With Depot, you can easily manage the state of your application, keeping track of changes and updating the user interface accordingly. Our library offers a simple and intuitive API, making it easy for developers of all levels to get started.

Whether you are building complex applications with hundreds of components or a simple application, Depot can help you manage state in a organized and scalable way. The library works seamlessly with popular frontend frameworks such as Roact, albeit with setup needed.

Installation

You can install Depot via NPM with the following command:

npm install @rbxts/depot

Usage

To use Depot in your project import the Depot class from the package:

import { Depot } from "@rbxts/depot";

Once you have the Depot class, you can create a new instance of it with initial state and mutator functions:

interface AppState {
	counter: number;
}

interface AppMutators {
	increment: (state: AppState) => AppState;
	decrement: (state: AppState) => AppState;
}

const appDepot = new Depot<AppState, AppMutators>({
	initialState: {
		counter: 0,
	},
	mutator: {
		increment: (state) => ({ ...state, counter: state.counter + 1 }),
		decrement: (state) => ({ ...state, counter: state.counter - 1 }),
	},
});

You can then access the current state of your application by calling the getState method:

const state = appDepot.getState(); // { counter: 0 }

You can update the state by calling the dispatch method with the name of the mutator and any additional payload:

appDepot.dispatch("increment");
const newState = appDepot.getState(); // { counter: 1 }

You can also listen for changes to the state by registering a callback with the listen method:

const unsubscribe = appDepot.listen((action, newState, oldState) => {
	print(action, newState, oldState);
});

appDepot.dispatch("increment"); // logs "increment", { counter: 1 }, { counter: 0 }

unsubscribe(); // Removes the listener

Combine Multiple Depots Into One

First you need to declare interfaces for the Combined State and Mutator. Let's say you have 2 depots, Money & Counter.

export type CombinedMutators = MoneyMutator & CounterMutator;
export interface CombinedState {
	Money: MoneyState;
	Counter: CounterState;
}

Then proceed to create a merged depot using the Combine static method.

import { Depot } from "@rbxts/depot";
import { CombinedMutators, CombinedState } from "...";

const mergedStore = Depot.Combine<CombinedState, CombinedMutators>({
	Money: MoneyDepot,
	Counter: CounterDepot,
});

Now, when you call the getState method, the state of the Money & Counter depots are there. Likewhise if you use the dispatch method, then it will autocomplete all the given mutator functions.

NOTE: Duplicate mutator function names will get merged, and thus wont call all of them. Make sure to avoid name duplication. This ties in into the core fundementals of structuring the states.

Middlewares

Note: Currently this only works for Combined Depots

Middlewares are essentially functions that can be used to intercept dispatches and either accept or reject it.

Some common use cases for middlewares could be replicating data to the client from the server, or validating that the data is correct.

Middlewares introduce side-effects and as such should be used with caution to ensure that you do not run into hard-to-catch bugs.

As aforementioned, middleware are functions, and thus in order to create one you write a function with the following signature:

NOTE: Middleware must be a 'async' function in order to work.

Middleware run before actually mutating the state, thus you can chose whether or not, inside of it, to set the new state. This is extremely powerful.

const myDepot = ...;

myDepot.addMiddleware(async(action, newState, oldState) => [
	print(action, newState, oldState);
	if (newState.exploiting) return false;

	return true;
])

When any action within the depot gets dispatched the middleware function we provided above will run. It will then print the action, newState, and oldState which you can use for whatever reason.

In this case, it has a property called Exploiting, which when true will not update the state.

Middlewares run sequentially depending on the order they were added.

API

new Depot<TState, TMutator>(Data: ConstructorData<TState, TMutator>): Depot

Creates a new instance of the Depot class with the given initial state and mutator functions.

depot.getState(): Readonly<TState>

Returns the current state of the depot.

depot.listen(callback: Listener<TState>): () => void

Registers a callback to be called whenever the state of the depot changes. Returns a function that can be called to unsubscribe the listener.

depot.dispatch<TKey extends Extract<keyof TMutator, string>>(Type: TKey, ...Payload: Payload<TKey, TMutator>): void

Dispatches a mutator in order to mutate the state and thus update it.

depot.flush(): void

Removes all registered listeners.

static Combine<TState extends object, TMutator extends object>(Map: DepotMap<TState>): CombinedDepot

Combines multiple depots into a single depot that can be used to manage multiple pieces of state at once.

addMiddleware(middleware: Middleware<TState>): void

Creates a new middleware and adds it to the depot.

About

Depot - A powerful and flexible state management library for modern applications.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published