Skip to content

Instantly share code, notes, and snippets.

@ludat
Last active August 26, 2020 20:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ludat/091edab24fe12d28930bd7b36dad16df to your computer and use it in GitHub Desktop.
Save ludat/091edab24fe12d28930bd7b36dad16df to your computer and use it in GitHub Desktop.
Example of a hook for loading things
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