Documentation
Getting started

nalanda Documentation

Nalanda is still in early release. We are working hard to make it production ready.

Installation

Use your preferred package manager to install:

# react codebase
npm install @nalanda/core @nalanda/react
 
# other codebases
npm install @nalanda/core

@nalanda/core is the core library that can be used with any framework. Read more about using Nalanda with other frameworks here.

Creating a Slice

Start by crafting a simple counter slice:

counterSlice.ts
import { createKey } from '@nalanda/react';
 
// The key is a local helper used to define various components of your slice.
const key = createKey('counterSlice', []);
 
// State fields define part of your state.
const counter = key.field(0);
 
// Actions define how a field/s should be updated.
function increment() {
  return counter.update((c) => c + 1);
}
 
// A slice serves as an interface, revealing the specified fields
// and actions to the entire application.
export const counterSlice = key.slice({
  counter,
  increment
});

Setting up the Store

At the root of your React app, set up a store and encapsulate your app with the StoreProvider component:

app.tsx
import { StoreProvider } from '@nalanda/react';
import { createStore } from '@nalanda/core';
 
import { counterSlice } from './counter-slice';
 
// Establish a global store incorporating your slices.
const store = createStore({
  slices: [counterSlice],
});
 
ReactDOM.render(
  <StoreProvider store={store}>
    <App />
  </StoreProvider>,
  document.getElementById('root')
);

Displaying the counter

With the store in place, employ the useSlice hook to access the state and actions from the slice:

counter.tsx
import { useTrack, useStore } from '@nalanda/react';
import { counterSlice } from './counterSlice';
 
export function Counter() {
  // useTrack will re-render the component any time the `counter` value changes.
  const { counter } = useTrack(counterSlice);
  const store = useStore();
 
  const increment = () => {
    // Dispatch an action to update the slice
    store.dispatch(counterSlice.increment());
  };
 
  return (
    <div>
      <h1>Counter</h1>
      <p>{counter}</p>
      <button onClick={increment}>increment</button>
    </div>
  );
}

Isn't That Quite a Bit for Just a Counter?

Before addressing that, consider the following code, inspired by a real-world application:

// Somewhere within a vast 5,000 lines of code
() => {
  counter += 1; // A simple counter increment
}

It seems straightforward, doesn’t it? Yet, ponder on these questions:

  1. What's the scope of the counter variable?
  2. Should other parts of the app have the capability to modify it?
  3. How can I prevent unintended modifications from other sections of the app?
  4. What if I decide to alter the counter's implementation? What other parts of the app will be affected?

While this might seem boilerplat-ish for a simple counter, it's essential to recognize that most applications are more complex than basic counters. They will contain many slices and numerous components, interacting with those slices in unforeseen ways. As your application evolves, you can add more slices to the store and integrate additional components to make use of them. Such a design ensures that, no matter how much your application grows, state management remains streamlined and more importantly intuitive.

The Complete Counter Example

Below is the full code for the counter demonstration. Feel free to interact and experiment with this example:

// counter-slice.js
import { useTrack, createKey, StoreProvider, useStore, createStore } from '@nalanda/react';
// key is local helper to define various things related to your slice
const key = createKey('counterSlice', []);

// state fields define the shape of your state
const counter = key.field(0);

// actions dicate how a field/s should be updated
function increment() {
  return counter.update((c) => c + 1);
}

// slice acts as an interface, exposing the chosen fields and actions to
// the rest of the application.
export const counterSlice = key.slice({
  counter,
  increment,
});

// app.js
const store = createStore({
  slices: [counterSlice],
});

export default function App() {
  return (
    <StoreProvider store={store}>
      <Counter />
    </StoreProvider>
  );
}

// counter.js
export function Counter() {
  // useTrack re-render the component whenever `counter` changes
  const { counter } = useTrack(counterSlice);
  const store = useStore();

  const increment = () => {
    // Dispatch an action to update the slice
    store.dispatch(counterSlice.increment());
  };

  return (
    <div>
      <h1>Counter</h1>
      <p>{counter}</p>
      <button onClick={increment}>increment</button>
    </div>
  );
}