State Management - DevNatan/inventory-framework GitHub Wiki

States are used to store data from a context. Previously, this data was obtained and defined through the get/set of a context using strings as keys, like a map.

States API is experimental and is not subject to the general compatibility guarantees, such API may be changed or may be removed completely in any further release.

Mutable State

The first state of the IF is the mutable state, the "mutable" is not explicit in its declaration because the developer is expected to use a mutable state most of the time.

This state has atomic update and can be used for anything whose update will update the inventory container.

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
Preview
private final MutableIntState counterState = mutableIntState();

@Override
public void onFirstRender(RenderContext render) {
    render.firstSlot()
        .watch(counterState)
        .rendered(() -> 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);

@Override
public void onFirstRender(RenderContext render) {
    render.firstSlot(new ItemStack(Material.ARROW))
        .onClick(click -> {
            int randomNumber = randomNumberState.get(click);
            Player player = click.getPlayer();
            
            player.sendMessage("Number: " + randomNumber);   
        });
}

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 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, 
    ImmutableMap.of(SOME_ID, "github")
);

Then the initial value of someData will ALWAYS be "github".

someId.get(holder); // "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);
Before After
context.get<User>("user");
State<User> userState = initialState(User.class);
userState.get(context);

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** ⚠️