Skip to content

Instantly share code, notes, and snippets.

@ccorcos
Created May 2, 2021 03:55
Show Gist options
  • Save ccorcos/21f50bae3553de1212fbfa9ebeaf1dcc to your computer and use it in GitHub Desktop.
Save ccorcos/21f50bae3553de1212fbfa9ebeaf1dcc to your computer and use it in GitHub Desktop.
// This object encapsulates the state and dispatches events.
class Store<T> {
constructor(public state: T) {}
public setState = (nextState: T) => {
this.state = nextState
this.emit()
}
private listeners = new Set<(state: T) => void>()
public addListener(fn: (state: T) => void) {
this.listeners.add(fn)
return () => this.listeners.delete(fn)
}
private emit() {
this.listeners.forEach(fn => fn(this.state))
}
}
// Hook that subscribes to a store.
function useStore<T>(store: Store<T>) {
const [state, setState] = useState(store.state)
useEffect(() => {
return store.addListener(setState)
}, [store])
return [state, store.setState] as const
}
// Initialize the global state here, wire up into context or pass through props
// but notice that the store reference never changes, so the props/context don't change either.
function App() {
const store = useMemo(() => new Store(0), [])
return (
<div>
<Slider store={store}/>
<Value store={store}/>
</div>
)
}
// Subscribe to the store inside.
function Slider({store}) {
const [value, setValue] = useStore(store)
return <input type="range" value={value} onChange={e => setValue(e.target.value)}/>
}
function Value({store}) {
const [value] = useStore(store)
return <div>{value}</div>
}
@ccorcos
Copy link
Author

ccorcos commented May 2, 2021

// Thing.ts, some global state thing.

export class Thing {}

// Environment.tsx, encapsulates all global state (possibly multiple things)

export type Environment = {thing: Thing}

export const EnvironmentContext = createContext<Environment | undefined>(undefined)

export function useEnvironment() {
	const environment = useContext(EnvironmentContext)
	if (!environment) {
		throw new Error("Missing EnvironmentContext.")
	}
	return environment
}


// main.ts
// Entry file should be the only place where you actuallt construct objects.
// This way, tree shaking works well (not relevant yet) and you won't run into 
// issues with circular dependencies (this can be really annoying) or have weird 
// bugs because files execute in a weird order.
const thing = new Thing()

const environment: Environment = { thing }

const root = document.getElementById("root")!
ReactDOM.render(
	<EnvironmentContext.Provider value={environment}>
		<App/>
	</EnvironmentContext.Provider>,
	root
)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment