Last active
September 7, 2024 13:09
-
-
Save nfour/92b4a869de7b4b8a847e2bb1eb8c2d53 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { component, css } from '__react' | |
import { AsyncValue, Value } from './models/state' | |
export const MyRandomNumberGenerator = component<{ | |
maximumGenerationAttempts: number | |
range: { from: number; to: number } | |
}>((props) => { | |
const { api } = useSomeRootState() | |
// `component.useState`, due to the name, gets picked up by react-refresh and operates correctly between hot-reloads | |
// Ideally this would be `component.state(() => ...)` | |
// Additionally, we are able to provide a `class` as the state initializer, | |
// which will be `makeAutoObservable`'d if there is no constructor present. | |
// With this we can achieve consistant syntax across all state definitions, whether local or global state | |
const state = component.useState( | |
() => | |
class { | |
// `Value` is just a mobx boxed store: { value: T, set: (v: T) => void } | |
// We make `props` observable so we can react to changes within this class, | |
// without complicated useEffects | |
props = new Value(props) | |
generationCount = new Value(0) | |
get isOutOfAttempts() { | |
return ( | |
this.generationCount.value >= | |
this.props.value.maximumGenerationAttempts | |
) | |
} | |
// AsyncValue is a mobx store which essentially wraps a async value ergonomically (similar to react-query?), | |
// providing .value, .error, .isPending, .query(), .set(), .progress, .clearQueue() etc. | |
// Types are also inferred nicely | |
myGeneratedNumber = new AsyncValue( | |
({ from, to }: { from: number; to: number }) => | |
api | |
.fetchSomeNumber({ from, to }) | |
.then((data) => data?.someNumber), | |
) | |
generateNumber = () => { | |
this.generationCount.set(this.generationCount.value + 1) | |
if (this.isOutOfAttempts) return | |
this.myGeneratedNumber.query({ | |
from: this.props.value.range.from, | |
to: this.props.value.range.to, | |
}) | |
} | |
}, | |
) | |
// Whenever a new react prop object values change occurs, | |
// update the Value store with only those changed | |
component.onProps(props, state.props) | |
// effectively `useEffect(() => fn(), [])` | |
component.onMounted(() => { | |
state.generateNumber() | |
}) | |
component.onUnmounted(() => { | |
// cleanup | |
}) | |
// mobx autorun when there is only 1 function argument | |
component.onReaction(() => { | |
console.log('fetchin number', state.myGeneratedNumber.isPending) | |
}) | |
// mobx reaction when there is 2 function arguments | |
component.onReaction( | |
() => state.myGeneratedNumber.value, | |
(newNumber) => { | |
console.log('cool a new number', newNumber) | |
}, | |
) | |
return ( | |
<> | |
{state.myGeneratedNumber.isPending && <div>Loading...</div>} | |
{state.myGeneratedNumber.error && ( | |
<div> | |
Something broke {state.myGeneratedNumber.error?.message} | |
</div> | |
)} | |
<div>Your number: {state.myGeneratedNumber.value}</div> | |
<button | |
disabled={ | |
state.myGeneratedNumber.isPending || state.isOutOfAttempts | |
} | |
onClick={state.generateNumber} | |
> | |
New number please! | |
</button> | |
<div> | |
Made {state.generationCount.value}/ | |
<span | |
css={css` | |
${state.isOutOfAttempts && | |
css` | |
color: red; | |
`} | |
`} | |
> | |
{props.maximumGenerationAttempts} | |
</span>{' '} | |
number generation requests so far. | |
</div> | |
{state.isOutOfAttempts && ( | |
<div | |
css={css` | |
color: red; | |
`} | |
> | |
Reached maximum generation attempts | |
</div> | |
)} | |
</> | |
) | |
}) | |
const useSomeRootState = () => { | |
return { | |
api: { | |
async fetchSomeNumber({ | |
from, | |
to, | |
}: { | |
from: number | |
to: number | |
}) { | |
return { | |
// number in range: | |
someNumber: Math.floor(Math.random() * (to - from) + from), | |
} | |
}, | |
}, | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment