state management - DevNatan/inventory-framework GitHub Wiki

States are a data manipulation feature used to store data from a context or a store data for specific viewer (a.k.a. player) in a context. In practical words it's an in-context map with a key and value, but, InventoryFramework created types for it.

Let's imagine a usage scenario

For a counter, we will have an "increment" button, a "decrement" button and an item that will represent the current value of the counter. The counter value is an integer so we have mutableIntState(initialValue) with a initial value of 0 for that.

1  class CounterView extends View {
2     private final MutableIntState counterState = mutableIntState(0);
3
4  }

States are defined during the initialization of your view so put them at the top-level of the class or in the constructor.

Now we are going to create the "increase" and "decrease" button, here there is no secret, just as it is explained in the Basic Usage guide. They will be both arrow.

1  class CounterView extends View {
2     private final MutableIntState counterState = mutableIntState(0);
3 
4     @Override
5     public void onFirstRender(RenderContext render) {
6         // Decrement button
7         render.slot(2, new ItemStack(Material.ARROW))
8             .cancelOnClick()
9             .onClick(counterState::increment);
10
11        // Increment button
12        render.slot(6, new ItemStack(Material.ARROW))
13            .cancelOnClick()
14            .onClick(counterState::decrement);
15     }
16  }

States are used to store data for a context.

Table of Contents

Mutable State

MutableState<String> textState = mutableState("");

textState.get(context); // ""
textState.set("abc", context);
textState.get(context); // "abc"

Real-world example

A counter whose value increases each time the player clicks on the item.

  1. watch Will trigger a update (re-render) on item every time counterState value changes.
  2. rendered Dynamic renderization so the item is always up to date with the new state value.
  3. counterState::increment Increments state value by 1 everyone the player clicks on it
private final MutableIntState counterState = mutableState(0);

@Override
public void onFirstRender(RenderContext render) {
    render.firstSlot()
        .watch(counterState)
        .renderWith(() -> new ItemStack(
            /* type = */ Material.DIAMOND,
            /* amount = */ counterState.get(render)
        ))
        .onClick(counterState::increment);   
}

Computed State

A computed state, is a state whose value that is returned when there is an attempt to obtain the value of that state is computed from its initial value.

The declaration is similar to the mutable state one except it asks for a Supplier instead of a static initial value.

State<Integer> randomNumberState = computedState(ThreadLocalRandom.current()::nextInt);

randomNumberState.get(host); // some random number
randomNumberState.get(host); // another random number

Real-world example

An item that sends a random number to the player with each click.

As it is a computed state each time the value is obtained a new value will be computed based on the factory provided when creating the state, in our case ThreadLocalRandom.current()::nextInt.

Preview
private final State<Integer> randomNumberState =
    computedState(() -> ThreadLocalRandom.current().nextInt(1, 64));

@Override
public void onFirstRender(RenderContext render) {
    render.firstSlot()
        .onRender(slotRender -> slotRender(new ItemStack(
            /* type */ Material.DIAMOND,
            /* amount */ randomNumberState.get(slotRender)
        ))
        .onClick(IFContext::updateSlot);
}

Lazy State

A lazy state is one whose value is only set the first time some object tries to get the value of that state.

In short, you define what the value will be, try to get the value, and the value obtained from there will be the value that will be obtained in subsequent calls to get the value of the state.

State<Integer> intState = lazyState(ThreadLocalRandom.current()::nextInt);

randomIntState.get(...); // 54 - from initial computation of random integer ^^
randomIntState.get(...); // 54 - previously defined by the initial computation

Initial State

The initial state is a immutable lazy state whose value is defined during the creation of the object that holds it.

For example: if the object that holds it is a ViewContext, the initial value of that state will be the initial data defined during the creation of the context, that is, when the view that the context originated from is opened for a player.

<T> State<T> initialState(String! key);

Example of using the initial state to store the id of an object initially defined when opening a context container

State<String> someId = initialState("some-id");

How someId is defined: before context creation, on open.

class MyAwesomeView extends View {

    private static final String SOME_ID = "some-id";

    final State<String> someId = initialState(SOME_ID);

}

Set it on open through initial data map.

viewFrame.open(
    MyAwesomeView.class, 
    player,
    ImmutableMap.of(SOME_ID, "github")
);

Migrating from get/set to states

All context.gets have been replaced by states, so you will no longer use the context as the source of the state, but rather as a reference to it.

Keep in mind that there are two variations of state: mutable and immutable, by default all states are immutable, and mutable states are explicitly defined by the prefix Mutable.

Before After
context.get<Integer>("my-awesome-context-key");
State<Int> myAwesomeState = mutableState(0);
myAwesomeState.get(context);

For initial values, set during view opening for a player were replaced by initialState.

The examples below using strings as keys are only supported due to backwards compatibility. It is not recommended to usekeys as a way to get the initial state, use a typed value instead.

Before After
context.get<User>("user");
State<User> userState = initialState("user");
userState.get(context);

You can pass an entire object now.

Before After
context.get<User>("user");
State<User> userState = initialState();
userState.get(context);
viewFrame.open(MyView.class, player, user);

Next Topics

You handle the data very well, congratulations! Now it is necessary to improve the player experience.
Organize your view using Pagination.

⚠️ **GitHub.com Fallback** ⚠️