DEV Community

Ribhararnus Pracutian
Ribhararnus Pracutian

Posted on

Build Your Own Magic Atomic State

⚛️ Solving Boilerplate with the Storeflow Hook

Why Another State Library?

If you've ever dealt with complex global or nested state in React, you know the routine was: Define an action, write a reducer, select the data... it's all tedious (even modern state manager like zustand / jotai). This boilerplate hits especially hard when you just want to update a single nested property like user.profile.name.

In this article, we're going to introduce Storeflow, a Zustand or Jotai alternative, it's a custom store hook built on React's useSyncExternalStore hook. Storeflow is designed to eliminate this boilerplate by automatically generating "Magic Setters" for every property—even nested ones—while providing powerful features like Middleware and Cross-Store Dependencies.

🚀 The Power of Storeflow!

Storeflow usees the native React hook philosophy to deliver a zero-boilerplate state solution. If you're ready to see how simple state management can be, check out the package and give the repository a star!

Resource Command/Link
Install via NPM npm install storeflow
Install via PNPM pnpm add storeflow
Source Code GitHub Repository 👈 Give it a ⭐️
Example https://storeflow-example.netlify.app/
Example Source Code Source code

Basic usage

import { store } from 'storeflow'

// store/use-post-store.ts
export const usePostStore = store({
   title: 'Initial title',
   content: 'Initial content'
   meta: {
     updatedAt: new Date()
   }
})

// components/component.ts
import { usePostStore } from 'store/use-post-store';
function Component() {
   const { title, $title, $meta_updatedAt } = usePostStore();
   return (
     <div className="flex items-center gap-2">
       <button onClick={() => $title('Hello world')}>{title}</button>
       <button onClick={() => $meta_updatedAt(new Date()}>Update Meta</button>
     </div>
   );
}
Enter fullscreen mode Exit fullscreen mode

Step 1: The Core - useSyncExternalStore

The core of Storeflow is the classic external store pattern, leveraging the power of the useSyncExternalStore hook (thanks to that). This powerful, native React Hook allows us to manage state outside of React components, trigger updates globally, and ensure only components subscribed to those specific changes are re-rendered.

The basic store structure defines the getters and the subscription mechanism required by the React runtime:

function store<T extends object>(initialState: T) {  
  let state = initialState;  
  let listeners = new Set<() => void>();

  const getState = () => state;

  const subscribe = (onStoreChange: () => void) => {  
    listeners.add(onStoreChange);  
    return () => listeners.delete(onStoreChange);  
  };  

  // The actual Storeflow hook exposed to components  
  const useStore = () => {  
    // This hook ensures component updates whenever listeners are notified  
    const currentState = useSyncExternalStore(subscribe, getState);   
    // ... magic setters will be injected here ...  
    return currentState;  
  };  

  // ... chaining methods (.effect, .middleware, .depends) ...  
  return useStore;  
}
Enter fullscreen mode Exit fullscreen mode

Step 2: The Magic - Dynamic Nested Setters

This is the key Storeflow feature where the boilerplate disappears. We want to be able to call setters like $title() or $meta_updatedAt() without manually writing them.

To achieve this, we need to solve a critical issue: calling React hooks (useCallback) inside a dynamic loop is a violation of the Rules of Hooks.

The Hooks Fix

We fix this by ensuring the number of setter paths is static and known before the component renders (based on the initial state shape). This allows us to safely inline the useCallback loop within the useStore custom hook:

// Calculated ONCE outside the hook/component  
const flattened = flattenObject(initialState);  
const setterPaths = Object.keys(flattened); 

// Inside the useStore hook:  
const useStore = (): StoreHookResult<T> => {  
    const currentState = useSyncExternalStore(subscribe, getState);  
    const hookSetters = {} as DynamicSetters<T>;  

    // FIX: The loop iterates over a static list (setterPaths) and is defined   
    // consistently within the hook body, satisfying React's rules.  
    for (const path of setterPaths) {  
        const setterName = `$${path.replace(/\\./g, '_')}`;

        const setterFn = (valueOrUpdater: any) => {  
            // Logic to handle value or updater function (v => v + 1)  
            // ...  
            _setPathValue(path, valueToSet); // Central update function  
        };

        // Hooks are called in a static, predictable order  
        (hookSetters as any)[setterName] = useCallback(setterFn, [path]);   
    }

    return { ...currentState, ...hookSetters };  
};
Enter fullscreen mode Exit fullscreen mode

This ensures React can track the hooks consistently, resolving the Rules of Hooks violation while delivering highly dynamic setter functions.

Step 3: Advanced Control - Side-Effects and Previous State

Storeflow allows you to chain configuration methods to your store definition for enhanced capabilities.

1. The .effect() Chaining Hook

The .effect() method registers a function that runs after every state change. This is the perfect place for asynchronous operations, data saving, or logging. It receives the currentState and a special prev getter for auditing.


// Define the effect when creating the store:  
const usePostStore = store({ title: 'Initial Title' })  
  .effect(async ([state], prev) => {  
    // Use dot notation to check any previous state value  
    const prevTitle = prev('title');   

    console.log('%c--- Post Store Effect Triggered ---', 'color: blue;');  
    if (prevTitle !== state.title) {  
      console.warn(`Title changed from "${prevTitle}" to "${state.title}"`);  
      // Example: Save to an API...  
      // await fetch('/api/save-post', { method: 'POST', body: JSON.stringify(state) });  
    }  
  });
Enter fullscreen mode Exit fullscreen mode

2. Middleware for Pre-Update Validation

Middleware intercepts the update before the state is committed. This is your chance to validate, transform, or even cancel the update chain entirely.

const useStoreWithMiddleware = store({ })
   .middleware([  
     // Middleware 1: Validation and Cancellation  
     (currentState, incomingUpdate, next) => {  
        const hasDataUpdate = Object.keys(incomingUpdate).some(path => path !== 'locked');

        if (currentState.locked && hasDataUpdate) {  
            console.error('Update CANCELLED: Store is locked.');  
            return next(false); // next(false) cancels the entire update  
        }  
        next();   
    },  

     // Middleware 2: Transformation and Audit  
     (currentState, incomingUpdate, next) => {  
        // Transform incoming 'data' to uppercase  
        if ('data' in incomingUpdate && typeof incomingUpdate.data \=== 'string') {   
          next({ data: incomingUpdate.data.toUpperCase() });   
        } else {  
          next(); // Pass update map unchanged  
        }  
     }  
   ]);
Enter fullscreen mode Exit fullscreen mode

Step 4: State Orchestration - Dependencies

The .depends() method allows a store's effect to subscribe to and react to changes in other Storeflow stores. This creates an explicit and clean data flow across your application.

When you define dependencies, the .effect() function receives the dependent stores' results (state + Magic Setters) as extra arguments in a tuple.


// 1. Define Store A  
const usePostStore = store({ content: '...' });

// 2. Define Store B, which depends on A  
const useAnotherOneStore = store({ count: 0 })  
    .depends([usePostStore]) // Register the dependency  
    .effect(([ownState, postStoreResult]) => {

        const { count } = ownState;

        // Access the Magic Setter of the dependent store\!  
        const { $content } = postStoreResult; 

        if (count === 5) {  
            console.log("Count reached 5! Updating Post Content directly.");  
            // Use the dependent store's setter to update its state  
            $content(`Content update triggered by dependency store: Count is ${count}.`);  
        }  
    });
Enter fullscreen mode Exit fullscreen mode

Conclusion

Storeflow demonstrates how to leverage powerful native React APIs like useSyncExternalStore and useCallback to build a robust, high-performance state solution that is perfectly aligned with modern React best practices. By focusing on zero boilerplate through dynamic setters and explicit data flow through chaining, you can write cleaner, more maintainable code without relying on bulky external libraries.

Give it a try in your next project! If you found this pattern valuable and plan to use Storeflow, please give the GitHub repository a star 🌟, it helps tremendously with visibility and future development!

Top comments (1)

Collapse
 
oknoorap profile image
Ribhararnus Pracutian

What patterns do you find most difficult to implement cleanly in standard React state? Let me know in the comments!