Skip to content

Instantly share code, notes, and snippets.

@jakoblorz
Created June 9, 2020 11:53
Show Gist options
  • Save jakoblorz/43460eb6bbd33f2f5e073b4fe82168f5 to your computer and use it in GitHub Desktop.
Save jakoblorz/43460eb6bbd33f2f5e073b4fe82168f5 to your computer and use it in GitHub Desktop.
OO-agnostic State Transformer à la Redux, integratable using React Hooks

TransformController.ts

OO-agnostic State Transformer à la Redux, integratable using React Hooks.

Inspired from "You might not need redux". While the presented architecture is reliable, in use cases where TypeScript is an option, one may want to use the type checking and not rely on string literals completely erasing type information during calls to state transformers. This solution is (if correctly integrated) OO-agnostic and persists type information.

interface IUserController {
handleSetName(name: string): void;
}
interface IUserControllerState {
name: string;
}
class UserController
extends TransformController<IUserControllerState, IUserController>
implements IUserController {
private static globalUserController = new UserController();
public static useController() {
return UserController.globalUserController.useController();
}
protected static SetNameTransformer(state: Readonly<IUserControllerState>, name: string): IUserControllerState {
return {
...state,
name,
};
}
constructor() {
super({ name: "" });
}
setName(name: string) {
this.dispatch(UserController.SetNameTransformer, name);
}
}
export const Example: React.FC<{}> => {
const userController = UserController.useController();
return (
<div>{userController.name}</div>
<button onClick={() => userController.setName("very cool user name")}>Generate a very cool user name</button>
);
};
import * as React from 'react';
import { useState, useMemo } from 'react';
export type Transformer<S> = (state: Readonly<S>, ...args: any) => S;
export type TransformerParameters<S, T extends Transformer<S>> = T extends (
state: Readonly<S>,
...args: infer P
) => any
? P
: never;
export abstract class TransformController<S, C> {
constructor(private readonly initialState: S) {}
private state: Readonly<S>;
private setState: React.Dispatch<React.SetStateAction<S>>;
public get(k: keyof S): Readonly<S>[keyof S] {
return this.state[k];
}
public useState(): Readonly<S> {
[this.state, this.setState] = useState(this.initialState);
return this.state;
}
public useController(): C & Readonly<S> {
const state = this.useState();
return useMemo(
(): C & Readonly<S> => ({
...((this as any) as C),
...state,
}),
[state]
);
}
protected dispatch<F extends Transformer<S>>(fn: F, ...args: TransformerParameters<S, F>) {
this.setState(fn(this.state, ...(args as any)));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment