State management in React as easy as possible
See more in the article: https://medium.com/@antonshanyuk/pubsub-as-an-alternative-of-redux-making-state-management-as-easy-as-possible-43216ccc83f1
yarn add rxjs pubsub-hooks
pubsub-hooks
references react
and rxjs
as peer dependencies. Make sure you installed them in you app
https://antonshanyuk.github.io/pubsub-hooks/?path=/story/pubsub-example--pub-sub
// pubSub.ts
import { createPubsub } from 'pubsub-hooks';
export default createPubsub({ counter: 0 });
//subscriberComponent.tsx
const SubscriberComponent = () => {
const counter = pubSub.useSub('counter');
return (
<div>{counter}</div>
);
}
//updateComponent.tsx
import pubSub from './pubSub.ts';
const UpdateComponent = () => {
// If you need to set a static value
const staticUpdate = pubSub.usePub('counter', () => 42);
// If you need an existing value for the update
const increment = pubSub.usePub('counter', x => x + 1);
// Only pass the key if the event sets value directly
// this equals to pubSub.usePub('counter', x => x);
const update = pubSub.usePub('counter')
return (
<>
<button onClick={staticUpdate}>Set 42</button>
<button onClick={increment}>Increment</button>
<button onClick={() => {
update(false);
}}>Set 34</button>
</>
);
}
If you use React.memo
for you components - make sure to wrap usePub
callbacks into React.useCallback
, especially if you use variables from the outer scope:
const [variable, setVariable] = React.useState(0);
...
const increment = pubSub.usePub('counter',
React.useCallback(() => variable, [variable]));
When creating a pubSub instance for your app you may want to have a possibility to check the state of your app. You may achive that by defining a global variable and passing it as an optional argument of createPubsub
:
import { createPubsub, SubjectsStorage } from 'pubsub-hooks';
const initialState = { counter: 0 };
// extending window interface
declare global {
interface Window {
pubsub: Record<string, SubjectsStorage<typeof initialState>>;
}
}
window.pubsub = {};
const pubSub = createPubsub<typeof initialState>(initialState, window.pubsub);
This way you will have the possibility to check the value in the console:
console.log(window.pubsub.counter.getValue());
Or subscribe for the updates:
window.pubsub.counter.subscribe((value) =>
console.log('updated value', value))
To ensure you dont have naming conflicts in your store, you may explicitly set enum for the storage keys:
const initialState = { counter: 0, showCounter: false };
enum StateKeys {
Counter = 'counter',
ShowCounter = 'showCounter',
};
const pubSub = createPubsub<typeof initialState, StateKeys>(initialState);
This way you'll be forced to use this enum everywhere in your app:
You may also pass the type for the storage explicitly instead of using typeof initialState
:
const initialState = {};
enum StateKeys {
Counter = 'counter',
};
type State = {
counter?: number;
};
const pubSub = createPubsub<State, StateKeys>(initialState);