Skip to content

State Management using custom hook without Wrapping components using Providers

Notifications You must be signed in to change notification settings

programming-with-ia/emittor

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

10 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Emittor

State Manager for 'ReactJs | NextJs'

β—¦ Developed with the software and tools below.

Next.js React TypeScript

git-last-commit GitHub commit activity GitHub top language

minified size

NPM Version GitHub


πŸ“ Overview

The custom emittor approach for state management in React offers a convenient way to manage and share states across multiple components without the need to wrap them with providers. By utilizing event-driven architecture, this method simplifies the handling of state updates, making it easy to work with event handlers and propagate changes throughout the application. With this approach, components can subscribe to state changes directly, ensuring seamless communication and synchronization between different parts of the UI. Additionally, it eliminates the boilerplate code associated with provider-based state management solutions, providing a more lightweight and flexible alternative for managing application state.


πŸš€ Getting Started

Install the package with npm:

npm install emittor

or yarn:

yarn add emittor

or pnpm:

pnpm add emittor

πŸ“– Usage

First Create Emittor in separate file /lib/emittor.ts

import { createEmittor } from 'emittor'

export const countEmittor = createEmittor(0)

then:
connect State with Emittor in client Components

"use client";
import { countEmittor } from "./lib/emittor";

function Page() {
    // connect State with `Emittor` in client Components and use like useState Hook
    const [count, setCount] = useEmittor(countEmittor)
    ...
}

This adjusted code snippet provides a simple and easy-to-understand usage example of the emittor package. It demonstrates using the useEmittor hook to manage state, similar to how useState is used in React.


Warning
Always create an emittor in a separate file
Because in development mode, the whole file will be rendered again. That can cause unexpected bugs.

Emittor In Multiple Components

The emittor package enables automatic state synchronization across React components. By simply integrating emittor, any component subscribing to state changes will update whenever the state changes anywhere in the application. This eliminates the need for manual state passing or provider wrapping, streamlining development and enhancing component responsiveness.

import { useEmittor } from "emittor"
import { countEmittor } from "./lib/emittor"

function Page(){
    return(
        <>
          <View />
          <Button/>
        </>
    )
}

// This component only re-renders when the state is changed.
function View(){
    const [count, setCount] = useEmittor(countEmittor)
    return(
        <div>
            {count}
        </div>
    )
}

function Button() {
  return (
    <button onClick={()=>countEmittor.setState(countEmittor.state+1)}>Add</button>
  )
}

export default Page

Other Usage

You can also use the emitter in event listeners or other functions to modify state.

countEmittor.getState()
countEmittor.setState(10)
// also use use directly
countEmittor.state
countEmittor.state = 10

It will re-render those components where that emitter is used.

Usage of ReducerEmittor

First Create ReducerEmittor in separate file /lib/emittor.ts

import { createReducerEmittor } from "emittor"

export const countREmittor = createReducerEmittor(0, {
    increment: e=> {
        e.setState(e.state+1)
    },
    decrement: e=> {
        e.setState(e.state-1)
    }
})

then use like this

"use client";
import { useEmittor } from "emittor"
import { countREmittor } from "./lib/emittor";

export default function Page() {
  const [count, setCount] = useEmittor(countREmittor)

  return (
    <div className="flex gap-8">
      <button className="bg-slate-700 p-3">
          {count}
      </button>
      <button className="bg-slate-700 p-3" onClick={()=>countREmittor.reducers.increment()}>
        +
      </button>
      <button className="bg-slate-700 p-3" onClick={()=>countREmittor.reducers.decrement()}>
        -
      </button>
    </div>
  );
}



you can also use Emittor like ReducerEmittor
in /lib/emittor.ts

import { createEmittor } from "emittor"

const emittor = createEmittor(0)

function increment(by:number){
    emittor.setState(emittor.state+by)
}

function decrement(by:number){
    emittor.setState(emittor.state-by)
}

export const countEmittor = {
    emittor,
    increment,
    decrement
}

use in Component

"use client";
import { useEmittor } from "emittor"
import { countEmittor } from "./lib/emittor";

export default function Page() {
  const [count, setCount] = useEmittor(countEmittor.emittor)

  return (
    <div className="flex gap-8">
      <button className="bg-slate-700 p-3" onClick={()=>setCount(count+1)}>
          {count}
      </button>
      <button className="bg-slate-700 p-3" onClick={()=>countEmittor.increment(10)}>
        +
      </button>
      <button className="bg-slate-700 p-3" onClick={()=>countEmittor.decrement(5)}>
        -
      </button>
    </div>
  );
}




API Reference

Method Description
constructor(initialState: T, options: { match?: boolean }) Initializes an instance of Emittor with an initial state and optional matching behavior. If options.match is true, setMatchState and emit are bound to handle state updates; otherwise, setOnlyState and emit are used.
setState(), emit() Sets the state and Executes all subscribed callbacks (also use state)
getState() Returns the current state (also use state).
state Is current state (with get & set) you can modify directly.
exec(), refresh() Executes all subscribed callbacks with the current state.
run(state: T) Executes all callbacks with the provided state (this will not changed current state).
connect(callback: Callback<T>) Adds the callback to the list of callbacks to be executed on state changes.
disconnect(callback: Callback<T>) Removes the specified callback from the list of callbacks.
*setOnlyState(state: T) Updates the state without checking and executes all subscribed callbacks.
*setMatchState(state: T) Updates the state only if the new state differs from the current state and then executes setOnlyState(state).

πŸ“„ License

This project is licensed under the ℹ️ MIT License.