DEV Community

Cover image for createState("Introducing AgileTs. A flexible State-Manager");
BennoDev
BennoDev

Posted on • Updated on

createState("Introducing AgileTs. A flexible State-Manager");

One of the most challenging problems to solve, especially in large frontend applications, is managing global States. While there are already several excellent approaches to solving global state management problems, most are tied to a specific workflow. You are often forced to define everything in a single source-of-truth store object, which takes away a lot of flexibility and simplicity. However, have you ever thought about managing your States as global individuals (atoms) that can be structured as preferred and dynamically bound to any UI-Component for reactivity?

I'm very excited to introduce you to AgileTs. A straightforward, flexible, well-tested State Management Library for Javascript/Typescript applications. AgileTs enables the straightforward creation of individual and independent States (createState('Hello World');) while providing a powerful toolset focused on developer experience around those States.

The flexibility provided by managing global States as individuals makes AgileTs suitable for both, developers building smaller applications (Style Guide) worrying about writing too much boilerplate code. And for teams creating large applications (Style Guide) trying to create readable, maintainable, and testable code.

Before we dive into a small example, it should be noted that there is no 'perfect' way of managing global States. Each State Management approach has benefits and drawbacks. Depending on the kind of application you are building and your preferred code style, you should weigh which State-Management-Library is best suited for your needs. More on the benefits and drawbacks of AgileTs later.

β€Ž

πŸ‘¨β€πŸ’» Get started with React and AgileTs

Let's see how AgileTs works with React. To demonstrate its basic capabilities, I will show you how to build a simple application using AgileTs and React. The sample project we'll look at is a small counter that lets us increase a number as we click the 'Update State' button. It may not be fascinating, but it shows all the essential pieces of a React + AgileTs application in action.

1️⃣ Installation

Installing AgileTs is as straightforward as installing any other npm packages. First, let’s install it using either npm or yarn. To properly work with AgileTs in a React environment, we need to add two different packages to our existing React application.

😎 If you want to set up a project from scratch, you can also use the official create-react-app template for AgileTs.


   // Javascript
   npx create-react-app my-app --template agile

   // Typescript
   npx create-react-app my-app --template agile-typescript

πŸ“ @agile-ts/core

npm install @agile-ts/core 
Enter fullscreen mode Exit fullscreen mode

The core package contains the state management logic of AgileTs and therefore offers powerful classes such as the State Class.

πŸ“‚ @agile-ts/react

npm install @agile-ts/react 
Enter fullscreen mode Exit fullscreen mode

The React Integration, on the other hand, is an interface to React and provides useful functions like the useAgile() Hook to easily bind States to React Components for reactivity.

2️⃣ Create State

const MY_FIRST_STATE = createState("Hello World");
Enter fullscreen mode Exit fullscreen mode

After we have successfully installed AgileTs, we can start creating our first independent AgileTs State. All you need to instantiate a State is to call createState() and specify an initial value.
In our example, we have assigned the initial value 'Hello World' to the State. If you are wondering why we write AgileTs States uppercase. Well, it has a simple advantage. We can easily differentiate between global and local States in our UI-Components (See Step 3️⃣).

3️⃣ Bind initialized State to a React-Component

const RandomComponent = () => {
    const myFirstState = useAgile(MY_FIRST_STATE); // <-

    return (
        <div>
            <p>{myFirstState}</p>
        </div>
    );
}
Enter fullscreen mode Exit fullscreen mode

Here (// <-) we bind our just created State to the React Component ('RandomComponent') using the useAgile() Hook. This binding ensures that the Component re-renders whenever the State value mutates. The useAgile() Hook returns the current value of the State. So in our case, something like 'Hello World'.

4️⃣ Update State value

 MY_FIRST_STATE.set(`Hello World ${++helloWorldCount}`);
Enter fullscreen mode Exit fullscreen mode

To bring some life into our small application, we update the State value with the help of the State's .set() function on each 'Update State' button press. Thereby we increase the external set helloWorldCount in ascending order.

😎 Everything put together

Here we see the whole counter-example in one piece.

// 2️⃣ Create State with the initial value "Hello World"
const MY_FIRST_STATE = App.createState("Hello World");

let helloWorldCount = 0;
const RandomComponent = () => {
    // 3️⃣ Bind initialized State to the 'RandomComponent' for reactivity
    const myFirstState = useAgile(MY_FIRST_STATE);

    return (
        <div>
            <p>{myFirstState}</p>
            <button
                onClick={() => {
                    // 4️⃣ Update State value on Button press
                    MY_FIRST_STATE.set(`Hello World ${++helloWorldCount}`);
                }}
            >
                Update State
            </button>
        </div>
    );
}
Enter fullscreen mode Exit fullscreen mode

If you are eager to learn more about AgileTs, take a look at our documentation.

β€Ž

πŸ‘¨β€πŸ’» Get started with [x] and AgileTs

Unfortunately, this blog post can't cover how to use AgileTs in other frontend frameworks than React, as that would be beyond the scope. However, the core principle of AgileTs is in each UI-Framework the same. The only part that might differ is how to bind States to UI-Components for reactivity (Step 3️⃣).

Here are code sandboxes for each already supported UI-Framework with the same counter-example as in the React example section above:

β€Ž

βš›οΈ Is AgileTs an atomic State Manager?

Yes, AgileTs follows the same pattern as atomic State Management Libraries like Recoil. States in AgileTs are created individually and lay above the UI-Layer, while they can be dynamically bound to any UI-Component (for example via Hooks).
In AgileTs, States are not called atoms, but rather individual or perhaps singleton States. However, the main difference to Recoil is that AgileTs doesn't depend on React, can be used outside the React-Tree, is more feature-rich and beginner-friendly.

β€Ž

πŸ‘ What makes AgileTs so special?

After our little excursion on how AgileTs works in React, we already understand its basic API and functionality. So let's talk about what exactly makes AgileTs so special and some benefits of using it.

πŸš… Straightforward

As you may have noticed in the React example above,
the API of AgileTs is fairly easy to understand and self-explaining. This is no coincidence; AgileTs is designed to write minimalistic, boilerplate-free code that captures your intent.

// Update State value to 'hi'
MY_STATE.set('hi'); 

// Undo latest State value change
MY_STATE.undo();

// Check if the State value is equal to '{hello: "jeff"}'
MY_STATE.is({hello: "jeff"}); 

// Reset State to its intial value
MY_STATE.reset(); 

// Preserves the State `value`  in the corresponding external Storage
MY_STATE.persist(); 

// Update State value in 200ms intervals
MY_STATE.interval((value) => value++, 200); 
Enter fullscreen mode Exit fullscreen mode

πŸ€Έβ€ Flexible

In AgileTs, States are created detached from each other and have an independent existence. Think of AgileTs States as global variables that can be structured as preferred and dynamically bound to any UI-Component. AgileTs States are partly like UI-Components since UI-Components are also just global variables embedded in other UI-Components.
image

The given flexibility has a lot of advantages. However, the capability to initialize States everywhere might lead to an unstructured and not transparent application, which quickly ends in a mess. To help you not to end up there, we have created some Style Guides to give you some inspiration on how to structure a frontend application using AgileTs.

🐻 Powerful State extensions

Based on the functionality of the basic AgileTs State, we have created further helpful classes, such as:

πŸ‘¨β€πŸ« Computed State

Computed States are a powerful concept that lets us build dynamic data depending on other data. To avoid unnecessary recomputations, the Computed Class caches the computed value and recomputes it only when an actual dependency has changed.

const INTRODUCTION= App.createComputed(() => {
   return `Hello I am '${MY_NAME.vale}'.`;
});
Enter fullscreen mode Exit fullscreen mode

A Computed magically tracks used dependencies (such as States) and automatically recomputes when one of its dependencies updates. In the above code snippet, it would, for example, recompute when the current value of MY_NAME changes from 'jeff' to 'hans'.

INTRODUCTION.value; // Returns "Hello I am 'jeff'."
MY_NAME.set('hans');
INTRODUCTION.value; // Returns "Hello I am 'hans'."
Enter fullscreen mode Exit fullscreen mode

Sandbox

πŸ‘¨β€πŸ‘©β€πŸ‘§ Collection State

Collection States come in handy when managing a set of information, such as a list of todos or users. A Collection is specially designed for arrays of data objects following the same pattern. Each of these data objects requires a unique item key to be correctly identified later. Think of a Collection like a database table that stores a data object once keyed by an id (item key).

const JOKES = App.createCollection();
Enter fullscreen mode Exit fullscreen mode

In the above example, we've created a Collection that stores a list of Jokes. However, a joke list without jokes isn't funny.
So let's add a funny joke to our newly created Joke Collection.

JOKES.collect({
  id: 1, 
  joke: "Why do Java programmers have to wear glasses?\n 
         Because they don't C#"
}, ['programming']);
Enter fullscreen mode Exit fullscreen mode

The joke we've just added belongs to the category 'Programming'. Therefore we categorize it to the programming Group. Groups allow us to easily cluster together data from a Collection as an array of item keys.

JOKES.getGroup('chucknorris').value; // Returns Chuck Norris Jokes
JOKES.getGroup('programming').value; // Returns Programming Jokes
JOKES.getDefaultGroup().value; // Returns All Jokes
Enter fullscreen mode Exit fullscreen mode

Sandbox

πŸš€ Enhance Performance

AgileTs assures performance optimization by batching re-render jobs and only re-rendering the UI-Components when an actual bound State mutates. You can go even further by only binding particular properties of a State value to the UI-Component or using the inbuilt proxy functionality.

// Component re-renders only when 'user.name' mutates
const name = useSelector(MY_USER, (value) => value.name);
console.log(name); // Returns 'jeff'

// Component re-renders only when 'user.age' mutates
const user = useProxy(MY_USER);
console.log(user.age); // Returns '8'
Enter fullscreen mode Exit fullscreen mode

🐞 Easy debugging

AgileTs has no advanced dev tools yet.
However, you can bind your States to the globalThis
and easily access them in the browser console.

const MY_STATE = createState('jeff');
const MY_COLLECTION = createCollection();

globalBind('__core__', {
  MY_STATE,
  MY_COLLECTION
});
Enter fullscreen mode Exit fullscreen mode

This allows you to preview and edit your global bound States at runtime. For example, the core of the AgileTs documentation is globally bound for better debugging. image Note that you should avoid attaching your application States to the globalThis in production because then third parties can easily interfere in your internal application logic. Since the AgileTs documentation has no vulnerable logic under the hood, the core is also accessible in production. Thus you can play around with the AgileTs documentation core and, for example, update the NPM_DOWNLOADS State or update the astronaut color.

__core__.stats.NPM_DOWNLOADS.set(999999);
Enter fullscreen mode Exit fullscreen mode

β€Ž

πŸ‘Ž Disadvantages of using AgileTs?

Like any other great global State Manager, also AgileTs comes with some drawbacks that we should talk about. We are working hard to reduce and get rid of these. If you have any further concerns about using AgileTs, let me know in the comments. Then I can list them here and maybe even counteract them 😁. Thanks for your support.
image

🐘 Big bundle size

Most State-Manager are pretty lightweight, but not this one. AgileTs has a minified size of 58.3kB (tree shaken 18kB) and is pretty heavy compared to its fellows. However, it offers a 100% type safety, a predictable runtime, an API focusing on developer experience, and much more in return. The large bundle size doesn't mean that AgileTs slows down your application noticeably. Convince yourself with the below listed AgileTs stress tests:

We have also created some benchmarks that compare different State Management approaches in terms of performance.

🌍 No large community

AgileTs hasn't been officially released until now (July 2021)
and I've not managed to build a community around the library yet. This was mainly because I thought AgileTs was not yet good enough to be shown to anyone. But well, among many other things I've learned while developing AgileTs, I've also learned that it's never too early to ask for feedback. πŸ˜…

If you want to become a part of the AgileTs community, don't hesitate to join our Community Discord. There you can ask anything related to AgileTs or programming in general and tell us what you think about AgileTs or what we can do better.

🌲 Only one contributor/maintainer

It may be strange, but if I (the only contributor) get hit by a tree or something and die, AgileTs will no longer have a maintainer. I've tried to create a as contributor-friendly codebase as possible. But still, it doesn't matter how many people are able to understand the code and fix the issues that might occur if no one can merge/release those changes.

β€Ž

❓ Conclusion

In conclusion, AgileTs provides a simple yet powerful API that focuses on developer experience and meets the need for small and large applications by being scalable without writing any boilerplate code. Therefore, AgileTs looks to be an excellent candidate to consider for State Management. Although it is not lightweight, it tries to optimize the performance of our applications wherever it can by batching re-renders and offering proxy-based functionalities like the useProxy() hook.

At last, thanks for taking the time to read this article. I would appreciate hearing what you think about AgileTs in the comments. In case you have any further questions, don't hesitate to join our Community Discord or ask on our subreddit. We are eager to help. And if you like the concept of AgileTs or/and want to support us, give us a ⭐️ (star) on Github and share it with your friends. Thanks for your support 😊

Cheers πŸŽ‰


Discussion (0)