6
\$\begingroup\$

Hey people on the internet!

I do not know where to share my new npm package so I am asking you do you know of a good place to share my package?

Also I would really appreciate if you take a look on it and give me your honest opinion on it react-spear

In short the goal of this package is to make state management more appealing then redux/MobX. It uses an object of store that contains Subjects that can subscribe to global value change and synchronize with local state.

I have made an effort to contribute to the developers community with this package and I hope it is a good one.

Please share with me your thoughts

If you have a counter like this:

src/Counter.(jsx/tsx)

import React from 'react';
import { Down } from './Down';
import { Up } from './Up';
import { Display } from './Display';
export const Counter = () => {
 return (
 <div>
 <Up />
 <Down />
 <Display />
 </div>
 );
};

That takes an Up and Down buttons compnent that needs to share their state. You will have a store like this:

src/store.(js/ts)

import { Subject } from 'react-spear';
export const Store = {
 counter: new Subject(0),
 // You can add Some other values here to the store,
 // and even an entire Store object to be nested here, whatever you want.
};

The Down and Up components should look like this:

src/Up.(jsx/tsx)

import React from 'react';
import { useSensable } from 'react-spear';
import { Store } from './store';
export const Up = () => {
 const count = useSensable(Store.counter);
 return <button onClick={() => Store.counter.broadcast(count + 1)}>Up {count}</button>;
};

src/Down.(jsx/tsx)

import React from 'react';
import { useSensable } from 'react-spear';
import { Store } from './store';
export const Down = () => {
 const count = useSensable(Store.counter);
 return <button onClick={() => Store.counter.broadcast(count - 1)}>Down {count}</button>;
};

And the Display component will look loke this:

src/Display.(jsx/tsx)

import React from 'react';
import { useSensable } from 'react-spear';
import { Store } from './store';
export const Display = () => {
 const count = useSensable(Store.counter);
 return (
 <div>
 <div>Count is {count}</div>
 </div>
 );
};

Explanation

  • When creating the store you are creating a subscribable object that can listen to changes.

  • When using the broadcast method(you can help me think other names if you don't like this one), you emit the next value of the state, actualy like setState but globally.

  • Then with the useSensable hook you are sensing the changes from the store(listening to broadcast event of that specific subject). Inside it uses useState to manage the update of the incoming new value, and useEffect to manage subscription to that value.

  • So everytime you broadcast a new value every component that consume that value via useSensable are getting rerendered with the new value.

Hope it make sense because it does to me.

Update

File structure:

src
├── hooks
│ └── useSensable.ts
├── lib
│ ├── StorageHandler.ts
│ └── Subscribable.ts
└── subjects
 ├── LocalSubject.ts
 ├── StorageSubject.ts
 ├── SessionSubject.ts
 └── Subject.ts

My code from the link above:

src/lib/Subscribable.ts

export type SubscribableHandler<T> = (nextValue: T) => void;
export interface ISubscribable<T> {
 subscribe: (handler: SubscribableHandler<T>) => () => void;
}
export class Subscribable<T> implements ISubscribable<T> {
 private _norifiers: SubscribableHandler<T>[] = [];
 protected broadcast(value: T) {
 this._norifiers.forEach((notifier) => notifier(value));
 }
 public subscribe = (handler: SubscribableHandler<T>) => {
 this._norifiers.push(handler);
 return () => {
 this._norifiers = this._norifiers.filter((_) => _ !== handler);
 };
 };
}

src/lib/StorageHandler.ts

export class StorageHandler {
 private storage: Storage;
 constructor(storage: Storage) {
 this.storage = storage;
 }
 public get<T>(key: string): T {
 const item = this.storage.getItem(key);
 return item ? JSON.parse(item) : null;
 }
 public set<T>(key: string, value: T): void {
 this.storage.setItem(key, JSON.stringify(value));
 }
 public getOrCreate<T>(key: string, defaultValue: T): T {
 let value: T = this.get<T>(key);
 if (value === null) {
 value = defaultValue;
 this.set(key, value);
 }
 return value;
 }
}

src/subjects/Subject.ts

import { ISubscribable, Subscribable } from '../lib/Subscribable';
export interface ISubject<T> extends ISubscribable<T> {
 value: T;
}
export class Subject<T> extends Subscribable<T> implements ISubject<T> {
 public value: T;
 constructor(initialValue?: T) {
 super();
 this.value = initialValue as T;
 }
 public broadcast(nextValue: T) {
 this.value = nextValue;
 super.broadcast(nextValue);
 }
}

src/subjects/StorageSubject.ts

 
import { StorageHandler } from '../lib/StorageHandler';
import { Subject } from './Subject';
export class StorageSubject<T> extends Subject<T> {
 private storageKey: string;
 private storage: StorageHandler;
 constructor(storage: StorageHandler, storageKey: string, initialValue?: T) {
 super(storage.getOrCreate(storageKey, initialValue));
 this.storageKey = storageKey;
 this.storage = storage;
 }
 public broadcast = (nextValue: T) => {
 this.storage.set(this.storageKey, nextValue);
 super.broadcast(nextValue);
 };
}

src/subjects/LocalSubject.ts

import { StorageHandler } from '../lib/StorageHandler';
import { StorageSubject } from './StorageSubject';
const localStorageManager = new StorageHandler(localStorage);
export class LocalSubject<T> extends StorageSubject<T> {
 constructor(storageKey: string, initialValue?: T) {
 super(localStorageManager, storageKey, initialValue);
 }
}

src/subjects/SessionSubject.ts

import { StorageHandler } from '../lib/StorageHandler';
import { StorageSubject } from './StorageSubject';
const sessionStorageManager = new StorageHandler(sessionStorage);
export class SessionSubject<T> extends StorageSubject<T> {
 constructor(storageKey: string, initialValue?: T) {
 super(sessionStorageManager, storageKey, initialValue);
 }
}

src/hooks/useSensable:

import { ISubject } from '../subjects/Subject';
import { useEffect, useState } from 'react';
export const useSensable = <T>(subject: ISubject<T>) => {
 const [value, setValue] = useState(subject.value);
 useEffect(() => subject.subscribe(setValue), [subject]);
 return value;
};
asked Oct 10, 2020 at 22:42
\$\endgroup\$
0

0

Know someone who can answer? Share a link to this question via email, Twitter, or Facebook.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.