Last active
July 24, 2019 07:55
-
-
Save tolotrasmile/bd3bd4b65e001cdcada1f4c3b48aba6a to your computer and use it in GitHub Desktop.
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
// hooks.js | |
import { useCallback, useEffect, useReducer, useRef } from "react"; | |
export function useRefMounted() { | |
// To prevent state update when component is unmounted | |
const isMounted = useRef(true); | |
useEffect(() => { | |
return () => { | |
// called when the component is going to unmount | |
isMounted.current = false; | |
}; | |
}, []); | |
return isMounted; | |
} | |
export function useStates(initialValues) { | |
const [state, dispatch] = useReducer( | |
// Merge old and new state | |
(oldState, newState) => ({ ...oldState, ...newState }), | |
initialValues | |
); | |
// To prevent state update when component is unmounted | |
const isMounted = useRefMounted(); | |
const setState = useCallback( | |
value => { | |
if (isMounted.current) { | |
dispatch(value); | |
} | |
}, | |
[dispatch, isMounted] | |
); | |
return [state, setState]; | |
} | |
// From react-use | |
const usePromise = () => { | |
const refMounted = useRefMounted(); | |
return useCallback( | |
promise => | |
new Promise((resolve, reject) => { | |
const onValue = value => { | |
if (refMounted.current) { | |
resolve(value); | |
} | |
}; | |
const onError = error => { | |
if (refMounted.current) { | |
reject(error); | |
} | |
}; | |
promise.then(onValue, onError); | |
}), | |
[refMounted] | |
); | |
}; | |
export function useFetch(url, { format = "json", options = {} }) { | |
const [state, setState] = useStates({ | |
data: null, | |
status: "idle", | |
error: null | |
}); | |
const makePromise = usePromise(); | |
const makeRequest = useCallback(async () => { | |
setState({ status: "loading" }); | |
try { | |
const data = await makePromise( | |
fetch(url, options).then(x => x[format]()) | |
); | |
setTimeout(() => { | |
setState({ data, status: "success", error: null }); | |
}, 2000); | |
} catch (error) { | |
setState({ status: "failure", error }); | |
} | |
}, [format, makePromise, options, setState, url]); | |
return [state, makeRequest]; | |
} | |
export function useYellowBox() { | |
useEffect(() => { | |
console.disableYellowBox = true; | |
return () => { | |
console.disableYellowBox = false; | |
}; | |
}, []); | |
} | |
// -------------------------------------------- | |
// Loader.js | |
import React, { createContext, useContext, useMemo, useState } from "react"; | |
const LoaderContext = createContext(); | |
function useLoader() { | |
const context = useContext(LoaderContext); | |
if (!context) { | |
throw new Error(`useLoader must be used within a LoaderContext`); | |
} | |
return context; | |
} | |
function LoaderProvider({ | |
children, | |
component: Component = Loader, | |
initialValue = false, | |
...props | |
}) { | |
const [loading, setLoading] = useState(() => initialValue); | |
const value = useMemo(() => setLoading, []); | |
return ( | |
<LoaderContext.Provider value={value} {...props}> | |
<Component loading={loading} /> | |
{children} | |
</LoaderContext.Provider> | |
); | |
} | |
export { LoaderProvider, useLoader }; | |
function Loader({ loading }) { | |
return loading ? ( | |
<div | |
style={{ | |
position: "absolute", | |
top: 0, | |
right: 0, | |
height: "100vh", | |
width: "100vw", | |
zIndex: 9999, | |
backgroundColor: "black", | |
opacity: 0.5, | |
display: "flex", | |
justifyContent: "center", | |
alignItems: "center" | |
}} | |
> | |
Loading | |
</div> | |
) : null; | |
} | |
Loader.defaultProps = { | |
loading: false | |
}; | |
export default Loader; | |
// -------------------------------------------- | |
// App.js | |
import React, { useEffect } from "react"; | |
import { useFetch } from "./hooks"; | |
import { useLoader } from "./Loader"; | |
function App() { | |
const url = "https://jsonplaceholder.typicode.com/todos"; | |
const [data, dispatch] = useFetch(url); | |
const setLoading = useLoader(); | |
useEffect(() => { | |
console.log("run effect"); | |
if (data) { | |
setLoading(data.status === "loading"); | |
} | |
}, [data, setLoading]); | |
return ( | |
<div> | |
<button onClick={dispatch}>Fetch</button> | |
<pre>{JSON.stringify(data, null, 2)}</pre> | |
</div> | |
); | |
} | |
export default App; | |
// -------------------------------------------- | |
// index.js | |
import App from "./App"; | |
import { LoaderProvider } from "./Loader"; | |
import React from "react"; | |
import ReactDOM from "react-dom"; | |
ReactDOM.render( | |
<LoaderProvider> | |
<App /> | |
</LoaderProvider>, | |
document.getElementById("root") | |
); | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment