noducs
TypeScript icon, indicating that this package has built-in type declarations

0.0.13 • Public • Published

noducs

noducs: No Ducks, Just State

noducs is a lightweight, boilerplate-free state management library for React applications. It simplifies global state management, offers seamless reactivity, supports middleware, and avoids unnecessary complexities like providers or extra wrapping components.


Table of Contents

  1. What Does It Do?
  2. Why No <Provider>?
  3. Features
  4. Installation
  5. Quick Start
  6. Middleware Support
  7. State Lifecycle
  8. Comparison with Other Libraries
  9. Best Practices
  10. FAQ
  11. Contributing
  12. License

Motivation

Managing state in React often feels more complicated than it needs to be. Libraries like Redux and Context API require a lot of setup—reducers, actions, dispatchers, and components—just to get started. For many projects, this can feel like too much overhead.

What if managing state was as simple as using a JavaScript object, but with the power of React's reactivity built in?

That’s the idea behind noducs. We wanted to make a state management library that’s simple, flexible, and fits into your workflow without getting in the way.

No Extra Complexity

With noducs, there’s no need for reducers or dispatch functions. You just create a slice of state and start using it.

import { createState } from "noducs";

// Create a simple counter state
const counterState = createState("counter", { count: 0 });

// Use the state in your component
export const useCounter = counterState.use;

// Actions to modify state
export const increment = () => counterState.set((state) => (state.count += 1));
export const decrement = () => counterState.set((state) => (state.count -= 1));

React Integration When You Need It

Use a for React-based global state when needed, or skip it entirely for simpler use cases.

import { StoreProvider } from "noducs";

const App = () => (
  <StoreProvider>
    <div>
      <h1>My App</h1>
    </div>
  </StoreProvider>
);

Or, skip the provider entirely:

import { storeManager } from "noducs";

// Access global state directly
console.log(storeManager.getState());

Simplicity

noducs automatically generates actions for every key in your state, so you don’t need to write them manually. Middleware lets you log, validate, or handle async actions with ease.

const userSlice = createSliceState({
  name: "user",
  initialState: { data: null, loading: false, error: null },
});

// Auto-generated actions
userSlice.actions.setData({ id: 1, name: "John Doe" });
userSlice.actions.setLoading(true);

// Add middleware
const loggerMiddleware = (state, updater, next) => {
  console.log("Before:", state);
  next(updater);
  console.log("After:", state);
};

Global State Without Fuss

Easily access and update global state anywhere in your app, even outside of React components.

import { storeManager } from "noducs";

// Access global state
const globalState = storeManager.getState();
console.log("Global State:", globalState);

// Update global state manually
globalState["user"].data = { id: 2, name: "Jane Doe" };
storeManager.updateGlobalState();

Built to Scale

Organize your app with modular state slices, and use middleware to handle more advanced use cases as your app grows.

const counterSlice = createSliceState({
  name: "counter",
  initialState: { count: 0 },
  middlewares: [
    (state, updater, next) => {
      console.log("Middleware - Before:", state);
      next(updater);
      console.log("Middleware - After:", state);
    },
  ],
});

counterSlice.actions.setCount(5); // Middleware logs will be triggered

noducs in a Nutshell

noducs takes the pain out of state management. Whether you’re working on a small feature or a large app with complex state, it’s designed to make managing your state simple, intuitive, and scalable.


What Does It Do?

  • Global State Management: Manage global state effortlessly across your React components, with or without a provider.

  • Direct State Access: No need for dispatch or useSelector—access state directly as JavaScript variables.

  • Middleware Support: Add middleware to log, validate, or handle asynchronous actions during state updates.

  • Optional : Use a component when you need React-based global state management, or skip it entirely for direct access.

  • React Integration: Components automatically re-render when the state they depend on changes, ensuring seamless updates.


🤔 How Global State and Providers Work

Unlike traditional libraries like Redux or Context API that enforce using a to wrap the entire application, noducs gives you the flexibility to choose how you manage your global state:

Default Behavior: No Provider Required

  • Independent State Management: Each slice is independently managed, and you can access or update it directly.
  • Global State Access: Use storeManager to access the entire global state without a .
  • Manual Updates (Optional): Directly update slices and notify subscribers when necessary.
import { storeManager } from "noducs";

// Access global state directly
console.log(storeManager.getState());

// Update global state manually
storeManager.getState()["user"].data = { id: 1, name: "John Doe" };
storeManager.updateGlobalState();

Using a for React-Based Global State

When you want React-based global state management, you can wrap any part of your app with a component. The provider makes the global store accessible to all its children using React Context.

How It Works:

Scoped Provider: You can wrap any part of your application—not just the root. Automatic Updates: State changes propagate to all components using the useGlobalStore hook.

import React from "react";
import { StoreProvider, useGlobalStore } from "noducs";

const GlobalViewer = () => {
  const globalState = useGlobalStore();
  return <pre>{JSON.stringify(globalState, null, 2)}</pre>;
};

const App = () => (
  <StoreProvider>
    <div>
      <h1>My App</h1>
      <GlobalViewer />
    </div>
  </StoreProvider>
);

export default App;

Flexibility to Mix Both Approaches

You can combine the two approaches:

Use storeManager for utility functions or areas without React Context. Use for scoped or React-friendly state access.

Why This Flexibility?

Large apps may need React-based state management in some areas while relying on direct access in others. Developers can start simple and scale as needed.

Manual State Updates with Global State

If you choose manual updates, you can control when global state updates propagate to subscribers. This is useful for optimizing performance in large applications.

// Perform multiple updates
storeManager.getState()["counter"].count += 1;
storeManager.getState()["user"].data = { id: 2, name: "Jane Doe" };

// Notify subscribers once
storeManager.updateGlobalState();

Key Features

Minimal Boilerplate: No reducers, no actions, no dispatch—just pure state management.
Auto-Generated Actions: Actions for updating state are automatically created based on the initial state.
Direct State Access: Access and update state as easily as working with JavaScript variables.
Reactivity Components re-render automatically when subscribed state changes.
Middleware Support: Intercept, log, validate, or handle asynchronous actions during state updates
Optional : Use React-based global state when needed, with flexible provider placement.
Easy Setup: Start managing state in minutes, with intuitive APIs and minimal configuration.
Redux DevTools Integration: Track and debug state changes effortlessly.


⚠️ Heads-Up!

You might notice that some names in the library, like createSliceState or references to "actions," feel a little like Redux. That’s because I’m still transitioning from my old Redux habits to fully embracing the simplicity of noducs. 😊

Think of it as a small homage to Redux while I find my rhythm with this library. Rest assured, everything is designed to be way simpler and more intuitive—no reducers, dispatchers, or boilerplate required!

📥 Installation

Using npm:

npm install noducs

Using yarn:

yarn add noducs

Quick Start

Getting started with noducs is easy. Here’s how you can create and use state slices, integrate middleware, and understand the library’s state lifecycle.

Create a State Slice

A state slice is a self-contained unit of state with actions and utilities for accessing or updating it.

import { createSliceState } from "noducs";

const counterSlice = createSliceState({
  name: "counter",
  initialState: { count: 0 },
});

export const useCounter = counterSlice.use;
export const { setCount } = counterSlice.actions;

export const increment = () => counterSlice.set((state) => (state.count += 1));

Use State in React Components

Access and update the state in your React components using the hooks and actions provided by your slice.

import React from "react";
import { useCounter, increment } from "./counterState";

const Counter: React.FC = () => {
  const [counter] = useCounter();

  return (
    <div>
      <h1>Count: {counter.count}</h1>
      <button onClick={increment}>Increment</button>
    </div>
  );
};

Access State Outside React

For scenarios where you need to use or manipulate state outside of React components, directly access the slice or global store.

import { storeManager } from "noducs";

storeManager.getState()["counter"].count += 1;
storeManager.updateGlobalState();
console.log(storeManager.getState());

Middleware Support

Middleware lets you intercept and manipulate state updates. You can log, validate, or perform asynchronous actions during updates.

Logger Middleware

Log every state update for debugging purposes.

// middleware/logger.ts
import { Middleware } from "noducs";

export const logger: Middleware<any> = (state, updater, next) => {
  console.log("🔄 Previous State:", state);
  next(updater);
  console.log("✅ Updated State:", state);
};

Async Middleware

Handle asynchronous logic like delays or API calls before updating state.

// middleware/async.ts
export const asyncMiddleware: Middleware<any> = async (
  state,
  updater,
  next
) => {
  console.log("⏳ Before Async Update");
  await new Promise((resolve) => setTimeout(resolve, 1000)); // Simulate delay
  next(updater);
  console.log("✅ After Async Update");
};

Apply Middleware

Add middleware when creating a state slice to enhance functionality.

// counterState.ts
import { createSliceState } from "noducs";
import { logger } from "./middleware/logger";
import { asyncMiddleware } from "./middleware/async";

const counterSlice = createSliceState({
  name: "counter",
  initialState: { count: 0 },
  middlewares: [logger, asyncMiddleware], // Attach middleware
});

State Lifecycle

1. State Initialization:

Define the initial state when creating a slice or state.

const initialState = { count: 0 };

2. State Updates:

Use set or actions to update the state.

counterSlice.set((state) => (state.count += 1)); // Direct update
counterSlice.actions.setCount(5); // Auto-generated action

3. Middleware Execution:

Middleware runs before state updates are applied, enabling logging, validation, or async handling.

const logger: Middleware<any> = (state, updater, next) => {
  console.log("Before Update:", state);
  next(updater);
};

4. Subscribers Notified:

After updates, all components or hooks using the state re-render automatically.


Comparison with other Libraries

Feature	                    noducs      	  Redux   	    Context API
Boilerplate     	        ❌ Minimal        	✅ High	        ✅ Moderate
<Provider>Needed	        ✅ Optional       	✅ Yes        	✅ Yes
Middleware Support	        ✅ Yes             	✅ Yes        	❌ No
Async Updates	            ✅ Yes           	✅ Yes        	❌ No
State Access	            ✅ Global/Direct 	✅ Global       	✅ Limited
Auto-Generated Actions	    ✅ Yes            	❌ No         	❌ No

Key Takeaways:

  • noducs stands out with optional usage and the ability to access global state directly.

  • Auto-generated actions reduce boilerplate significantly compared to Redux.

  • Context API lacks middleware and async handling, which noducs provides out of the box.


Best Practices

Keep State Slices Modular:

Focus each state slice on a specific domain or feature for better scalability and maintainability.

Leverage Middleware:

Use middleware to log state changes, validate updates, and handle asynchronous operations.

Avoid Deeply Nested State Objects:

Keep your state structure flat to simplify updates and improve performance.

Mix Direct Access and Provider:

Use direct global store access (storeManager) for utility functions or outside React, and for React-based state management.


FAQ

Do I need a to use this library?

No, you don’t need a . State slices are independently managed, and you can access the global store directly using storeManager. However, the is available for React-based global state management when needed.

Can I use it with Next.js?

Absolutely. noducs works with both SSR and client-side rendering. Use storeManager for SSR-friendly state access and for React components.

Is it scalable?

Yes, noducs is highly scalable. You can create multiple state slices with middleware and async support without any performance bottlenecks.

Does it work with Redux DevTools?

Yes, Redux DevTools is fully integrated. You can track and debug state changes effortlessly.


Contributing

We’d love for you to contribute and help make noducs even better! Whether it’s fixing a bug, adding a feature, or improving the documentation, your contributions are always welcome. Here’s how you can get started:

Fork the Repository: Create your own copy of the repo to work on.

Submit a Pull Request: Once you’ve made your changes, send us a pull request. We’ll review it and work with you to get it merged.

Report Bugs or Suggest Features: Found a bug? Have an idea for a cool new feature? Let us know by creating an issue!

We’re excited to have you join the journey—let’s build something amazing together!


📜 License

noducs is licensed under the MIT License.

Readme

Keywords

Package Sidebar

Install

npm i noducs

Weekly Downloads

14

Version

0.0.13

License

ISC

Unpacked Size

48.9 kB

Total Files

19

Last publish

Collaborators

  • ajanrp