I’m currently building my auth system (yes, from the other post), but I got chained in a Promise.t mess (kinda normal for javascript).
Right now I have this:
fetch(...)
->Promise.then(Response.json)
->Promise.thenResolve(json => {
let val = JSON.Decode.object(json)->Option.getOrThrow
// let user = User.fromJson(val)
Console.log(val)
// TODO: I should set the state to AuthenticatedWithData(user)
})
But I need that AuthenticatedWithData variant in the outer scope, not in the promise scope. I’m inside of a reducer, so I can’t use any react tricks for it, is there anything I can do to fix that?
Or is there any javascript binding I can write to fix that? I’m kinda confused on that.
I’m not in a async context to use await, i’m in a reducer.
About the dispatching inside a promise, it’s a bit more complicated than that. I think I should just put promise everywhere in my code and make everything async by default, dunno.
I’m kinda lost on how to structure my code to begin with. I probably just need more experience with it.
Well in general you should not trigger side effects from inside a reducer. So reducers are usually clean from async functions and they should be that way.
If a reducer action triggers a change in the state, that state can be a dependency of an useEffect for instance, which is where you should trigger your (async) side effects such as fetches.
It also helps to enumerate the possible states of your component:
type state = Loading | Loaded(User.t)
type action = SetState(state)
let reducer = (state, action) =>
switch action {
| SetState(state) => state
}
@react.component
let make = () => {
let (state, dispatch) = React.useReducer(reducer, Loading)
React.useEffect(() => {
fetch("")
->Promise.then(Response.json)
->Promise.thenResolve(json => {
let val = JSON.Decode.object(json)->Option.getOrThrow
let user = User.fromJson(val)
dispatch(SetState(Loaded(user)))
})
->Promise.ignore
None
}, [dispatch]
switch state {
| Loaded(user) => <div> {React.string("User loaded")} </div>
| Loading => <div> {React.string("Loading...")} </div>
}
}
That said, I very often use a trick to enforce async context when I need it:
module PromiseUtils = {
let run = (makePromise: unit => promise<unit>) =>
makePromise()->Promise.ignore
}
let mySyncFn = () =>
PromiseUtils.run(async () => {
let json = await fetch("")->Promise.then(Response.json)
let val = JSON.Decode.object(json)->Option.getOrThrow
let user = User.fromJson(val)
dispatch(SetState(Loaded(user)))
})