Skip to content

Instantly share code, notes, and snippets.

@pridees
Created August 19, 2021 08:34
import logo from './logo.svg';
import './App.css';
import { useCallback, useEffect, useState } from 'react'
type ObjectDidChange = (forceUdpateValue: number) => void
type ForceUpdateKey = number
class Model {
private forceUdpateKey: number = 0
public objectDidChangeListeners: ObjectDidChange[] = []
public notify() {
this.forceUdpateKey += 1
this.objectDidChangeListeners.forEach(callback => void callback(this.forceUdpateKey))
}
}
class CounterViewModel extends Model {
private count: number;
constructor(initialState: { counter: number }) {
super();
this.count = initialState.counter;
}
get counter() {
return this.count;
}
public setCounter(newVal: number) {
this.count = newVal;
}
}
type Setter<T = Model> = (setter: (model: T) => void) => void
function useViewModel<T extends Model>(model: T): [T, Setter<T>, ForceUpdateKey?] {
const [, setForceUpdateKey] = useState<ForceUpdateKey>(0)
useEffect(() => {
const handleForceUpdate = (forceUpdateValue: number) =>
void setForceUpdateKey(forceUpdateValue)
model.objectDidChangeListeners.push(handleForceUpdate)
return () => {
model.objectDidChangeListeners = model.objectDidChangeListeners.filter(v => v !== handleForceUpdate)
}
}, [model])
const setProperty = useCallback((setter: (model: T) => void) => {
setter(model)
model.notify()
},
[model]
);
return [model, setProperty];
};
const counterViewModel = new CounterViewModel({ counter: 0 })
const Counter = () => {
const [model, setProperty] = useViewModel<CounterViewModel>(counterViewModel)
const increment = useCallback(() => {
setProperty((model) => void model.setCounter(model.counter + 1))
}, [setProperty])
return (
<>
<h1>Counter {model.counter}</h1>
<button onClick={increment}>increment</button>
</>
)
}
function App() {
return (
<div className="App">
<header className="App-header">
<Counter />
<img src={logo} className="App-logo" alt="logo" />
<Counter />
</header>
</div>
);
}
export default App;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment