Last active
August 26, 2020 20:48
-
-
Save ludat/091edab24fe12d28930bd7b36dad16df to your computer and use it in GitHub Desktop.
Example of a hook for loading things
This file contains 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 * as React from "react"; | |
import "./styles.css"; | |
import axios from "axios"; | |
enum RequestStateKind { | |
LOADING, | |
SUCCESS, | |
NOT_ASKED, | |
ERROR | |
} | |
type LoadingState = { state: RequestStateKind.LOADING }; | |
type ErrorState = { state: RequestStateKind.ERROR; error: string }; | |
type NotAskedState = { state: RequestStateKind.NOT_ASKED }; | |
type SuccessState<T> = { state: RequestStateKind.SUCCESS; value: T }; | |
type RequestState<T> = | |
| LoadingState | |
| ErrorState | |
| NotAskedState | |
| SuccessState<T>; | |
function isSuccess<T>( | |
requestState: RequestState<T> | |
): requestState is SuccessState<T> { | |
return requestState.state === RequestStateKind.SUCCESS; | |
} | |
function useRequest<T>(f: () => Promise<T>, deps: unknown[]): RequestState<T> { | |
const [result, setResult] = React.useState<RequestState<T>>({ | |
state: RequestStateKind.NOT_ASKED | |
}); | |
React.useEffect(() => { | |
const main = async () => { | |
try { | |
setResult({ state: RequestStateKind.LOADING }); | |
const r = await f(); | |
setResult({ state: RequestStateKind.SUCCESS, value: r }); | |
} catch (e) { | |
setResult({ state: RequestStateKind.ERROR, error: e }); | |
} | |
}; | |
main(); | |
}, deps); | |
return result; | |
} | |
type Todo = {userId: number, id: number, title: string, completed: boolean}; | |
class ThingService { | |
// This is unsafe since I'm casting Todo from raw json. | |
async getTheThing(id: number): Promise<Todo> { | |
const r = await axios.get( | |
`https://jsonplaceholder.typicode.com/todos/${id}` | |
); | |
return r.data; | |
} | |
} | |
export default function App() { | |
const [id, setId] = React.useState<number>(1); | |
// Here I emulate changing props and such | |
React.useEffect(() => { | |
const timeout = setTimeout(() => { | |
setId(id + 1); | |
}, 1000); | |
return () => clearTimeout(timeout); | |
}, [id]); | |
// one disadvantage here is that eslint can't know that the array is a dependencies array | |
const result = useRequest(() => | |
new ThingService().getTheThing(id), [id]); | |
// Three ways to get the value without typescript complaining | |
// Using an if and handling all that non success cases in the same way | |
// even the loading state | |
// if (!isSuccess(result)) { | |
// return <div>Loading</div>; | |
// } | |
// and here typescript knows that result must be success | |
// return ( | |
// <div className="App"> | |
// <h1>Hello {JSON.stringify(result.value)}</h1> | |
// <h2>Start editing to see some magic happen!</h2> | |
// </div> | |
// ); | |
// The most obvious one, using a big case where we handle each | |
// case separately, here we can use the error as well as the value | |
switch (result.state) { | |
case RequestStateKind.ERROR: { | |
return ( | |
<div className="App"> | |
<h1>Something failed</h1> | |
<h2>{result.error}</h2> | |
</div> | |
); | |
} | |
case RequestStateKind.LOADING: { | |
return ( | |
<div className="App"> | |
<h1>Loading</h1> | |
</div> | |
); | |
} | |
case RequestStateKind.NOT_ASKED: { | |
return ( | |
<div className="App"> | |
<h1>Did not asked yet</h1> | |
</div> | |
); | |
} | |
case RequestStateKind.SUCCESS: { | |
return ( | |
<div className="App"> | |
<h1>{result.value.title} ({result.value.id})</h1> | |
<h2>Start editing to see some magic happen!</h2> | |
</div> | |
); | |
} | |
} | |
// Here we do many if statements to check the result and | |
// as well as the switch we can use the error as well as the value | |
// if (result.state === RequestStateKind.ERROR) { | |
// return ( | |
// <div className="App"> | |
// <h1>Something failed</h1> | |
// <h2>{result.error}</h2> | |
// </div> | |
// ); | |
// } | |
// if (result.state === RequestStateKind.LOADING) { | |
// return ( | |
// <div className="App"> | |
// <h1>Loading</h1> | |
// </div> | |
// ); | |
// } | |
// if (result.state === RequestStateKind.NOT_ASKED) { | |
// return ( | |
// <div className="App"> | |
// <h1>Did not asked yet</h1> | |
// </div> | |
// ); | |
// } | |
// if (result.state === RequestStateKind.SUCCESS) { | |
// return ( | |
// <div className="App"> | |
// <h1>Hello {JSON.stringify(result.value)}</h1> | |
// <h2>Start editing to see some magic happen!</h2> | |
// </div> | |
// ); | |
// } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment