Skip to content

Instantly share code, notes, and snippets.

@MarkusWendorf
Last active Nov 2, 2020
Embed
What would you like to do?
React global state via simple subscription hook
import React, { useEffect, useState } from "react";
/* === Implementation / usage below === */
export class Store<S> {
private state: S;
private subscriptions: Array<(state: S) => void>;
constructor(initialState: S) {
this.state = initialState;
this.subscriptions = [];
}
private subscribe = (cb: (state: S) => void) => {
this.subscriptions.push(cb);
};
update = (updater: (state: S) => void) => {
updater(this.state);
this.state = { ...this.state };
this.subscriptions.forEach((cb) => cb(this.state));
};
createHook = () => {
return () => {
const [localState, setLocalState] = useState(this.state);
// setup subscription
useEffect(() => {
const cb = (state: S) => setLocalState(state);
this.subscribe(cb);
return () => {
this.subscriptions = this.subscriptions.filter((subscription) => subscription !== cb);
};
}, []);
type Actions = Omit<this, "subscribe" | "createHook">;
return [localState, this] as [Readonly<S>, Actions];
};
};
}
/* === USAGE === */
interface Todo {
userId: number;
id: number;
title: string;
completed: boolean;
}
interface UserState {
todos: Todo[];
}
class TodoStore extends Store<UserState> {
addTodo = (todo: Todo) => {
this.update((state) => {
state.todos.push(todo);
});
};
setTodos = async () => {
const res = await fetch("https://jsonplaceholder.typicode.com/todos");
const data = await res.json();
this.update((state) => {
state.todos = data;
});
};
}
const userStore = new TodoStore({ todos: [] });
export const useUserStore = userStore.createHook();
// Usage in component
const A = () => {
const [state, actions] = useUserStore();
return (
<h1 onClick={() => actions.addTodo({ completed: false, id: Math.random(), title: "Titl", userId: Math.random() })}>
Click
{JSON.stringify(state.todos)}
</h1>
);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment