From 406ffd7d3d430a5f91328e85a5475a9cfdc596c4 Mon Sep 17 00:00:00 2001 From: sagar Date: Thu, 9 Jan 2020 15:45:48 +0530 Subject: [PATCH 1/7] =?UTF-8?q?=F0=9F=93=A6=20NEW:=20useAsync=20hook=20add?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- snippets/UseAsync.md | 72 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 snippets/UseAsync.md diff --git a/snippets/UseAsync.md b/snippets/UseAsync.md new file mode 100644 index 000000000..5f717044a --- /dev/null +++ b/snippets/UseAsync.md @@ -0,0 +1,72 @@ +--- +title: useAsync +tags: react,intermediate,hook +--- + + +The `useAsync` hook is used to bind asynchronous call and handle different states like loading, error, and value. According to the state of an asynchronous call, we can render different states like loading, error and value state. + +**Explanation:** +In `useAsync` hook we need to pass the handler function and that will be called after calling the `run` method. Once `run` get's called we're changing loading, error and value field according to asynchronous call response. + +```js +const useAsync = (fn, options = {}) => { + const autoRun = options.autoRun || false + + const [state, setState] = React.useState({ + error: null, + loading: false, + value: null, + }) + + const handleStateChange = args => { + setState({ ...state, ...args }) + } + + const run = async (args = null) => { + try { + handleStateChange({ loading: true, error: null }) + const value = await fn(args) + handleStateChange({ loading: false, value: value || null, error: null }) + } catch (error) { + handleStateChange({ loading: false, value: null, error }) + } + } + + React.useEffect(() => { + if (autoRun) { + run() + } + }, [autoRun]) + + return { + state, + setState, + run, + } +} +``` + +**Usages** +```jsx +const App = () => { + const handleSubmit = () => { + const url = "https://jsonplaceholder.typicode.com/todos" + return fetch(url).then(response => response.json()) + } + + const submission = useAsync(handleSubmit, { autoRun: false }) + + return ( +
+ {submission.value &&
{submission.value}
} + +
+ ) +} +``` From ea39369af79df8878e37931c5635809972af5ce8 Mon Sep 17 00:00:00 2001 From: sagar Date: Thu, 9 Jan 2020 20:21:10 +0530 Subject: [PATCH 2/7] =?UTF-8?q?=F0=9F=90=9B=20FIX:=20PR=20comments=20fixed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- snippets/UseAsync.md | 54 ++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 29 deletions(-) diff --git a/snippets/UseAsync.md b/snippets/UseAsync.md index 5f717044a..e4242318e 100644 --- a/snippets/UseAsync.md +++ b/snippets/UseAsync.md @@ -1,35 +1,34 @@ --- title: useAsync -tags: react,intermediate,hook +tags: hooks,state,effect,intermediate --- +A hook that handle asynchronous calls. -The `useAsync` hook is used to bind asynchronous call and handle different states like loading, error, and value. According to the state of an asynchronous call, we can render different states like loading, error and value state. +- Create a custom hook that takes a handler `function` and `options`. +- Use the `React.useState()` hook to initialize the `value`, `error` and `loading` state variables. +- Use the `React.useEffect()` hook to call `run()` method and update the state variables accordingly if `options.autoRun` set to true. +- Use the `run` function to manually trigger `handler` function. +- Return an object containting the `value`, `error` and `isLoading` state variables and `run` function. -**Explanation:** -In `useAsync` hook we need to pass the handler function and that will be called after calling the `run` method. Once `run` get's called we're changing loading, error and value field according to asynchronous call response. - -```js +```jsx const useAsync = (fn, options = {}) => { + const [value, setValue] = React.useState(null) + const [error, setError] = React.useState(null) + const [isLoading, setIsLoading] = React.useState(false) + const autoRun = options.autoRun || false - const [state, setState] = React.useState({ - error: null, - loading: false, - value: null, - }) - - const handleStateChange = args => { - setState({ ...state, ...args }) - } - const run = async (args = null) => { try { - handleStateChange({ loading: true, error: null }) + setIsLoading(true) const value = await fn(args) - handleStateChange({ loading: false, value: value || null, error: null }) + setError(null) + setValue(value) } catch (error) { - handleStateChange({ loading: false, value: null, error }) + setIsLoading(false) + setValue(null) + setError(error) } } @@ -40,17 +39,17 @@ const useAsync = (fn, options = {}) => { }, [autoRun]) return { - state, - setState, + value, + error, + isLoading, run, } } ``` -**Usages** ```jsx const App = () => { - const handleSubmit = () => { + const handleSubmit = (args) => { // args {foo: bar} const url = "https://jsonplaceholder.typicode.com/todos" return fetch(url).then(response => response.json()) } @@ -59,13 +58,10 @@ const App = () => { return (
- {submission.value &&
{submission.value}
} - +
{JSON.stringify(submission.value, null, 2)}
) } From ea559a1e668e0902e37188d6029c38bc4afa85a1 Mon Sep 17 00:00:00 2001 From: sagar Date: Thu, 9 Jan 2020 20:26:38 +0530 Subject: [PATCH 3/7] =?UTF-8?q?=F0=9F=90=9B=20FIX:?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- snippets/UseAsync.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/snippets/UseAsync.md b/snippets/UseAsync.md index e4242318e..561582f2f 100644 --- a/snippets/UseAsync.md +++ b/snippets/UseAsync.md @@ -23,12 +23,13 @@ const useAsync = (fn, options = {}) => { try { setIsLoading(true) const value = await fn(args) + setIsLoading(false) setError(null) setValue(value) } catch (error) { setIsLoading(false) - setValue(null) setError(error) + setValue(null) } } From 19f41b21e2330d42c45f7e46889d61b6f640f913 Mon Sep 17 00:00:00 2001 From: sagar Date: Thu, 9 Jan 2020 20:28:00 +0530 Subject: [PATCH 4/7] isLoading set to false --- snippets/UseAsync.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snippets/UseAsync.md b/snippets/UseAsync.md index 561582f2f..4c43ace15 100644 --- a/snippets/UseAsync.md +++ b/snippets/UseAsync.md @@ -50,7 +50,7 @@ const useAsync = (fn, options = {}) => { ```jsx const App = () => { - const handleSubmit = (args) => { // args {foo: bar} + const handleSubmit = (args) => { // args { foo: bar } const url = "https://jsonplaceholder.typicode.com/todos" return fetch(url).then(response => response.json()) } From 103bb106392438ac55d1918b99a30de54abdf235 Mon Sep 17 00:00:00 2001 From: sagar Date: Thu, 9 Jan 2020 22:01:21 +0530 Subject: [PATCH 5/7] =?UTF-8?q?=F0=9F=90=9B=20FIX:=20typo=20handle=20=3D>?= =?UTF-8?q?=20handles?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- snippets/UseAsync.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snippets/UseAsync.md b/snippets/UseAsync.md index 4c43ace15..9a6b2a7d3 100644 --- a/snippets/UseAsync.md +++ b/snippets/UseAsync.md @@ -3,7 +3,7 @@ title: useAsync tags: hooks,state,effect,intermediate --- -A hook that handle asynchronous calls. +A hook that handles asynchronous calls. - Create a custom hook that takes a handler `function` and `options`. - Use the `React.useState()` hook to initialize the `value`, `error` and `loading` state variables. From 80414e1f9c0f5a53bb45aeda65912b5f4df6441a Mon Sep 17 00:00:00 2001 From: sagar Date: Thu, 9 Jan 2020 22:05:12 +0530 Subject: [PATCH 6/7] =?UTF-8?q?=F0=9F=91=8C=20IMPROVE:=20semicolons=20add?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- snippets/UseAsync.md | 56 ++++++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/snippets/UseAsync.md b/snippets/UseAsync.md index 9a6b2a7d3..8c3ac1416 100644 --- a/snippets/UseAsync.md +++ b/snippets/UseAsync.md @@ -13,57 +13,61 @@ A hook that handles asynchronous calls. ```jsx const useAsync = (fn, options = {}) => { - const [value, setValue] = React.useState(null) - const [error, setError] = React.useState(null) - const [isLoading, setIsLoading] = React.useState(false) + const [value, setValue] = React.useState(null); + const [error, setError] = React.useState(null); + const [isLoading, setIsLoading] = React.useState(false); - const autoRun = options.autoRun || false + const autoRun = options.autoRun || false; const run = async (args = null) => { try { - setIsLoading(true) - const value = await fn(args) - setIsLoading(false) - setError(null) - setValue(value) + setIsLoading(true); + const value = await fn(args); + setIsLoading(false); + setError(null); + setValue(value); } catch (error) { - setIsLoading(false) - setError(error) - setValue(null) + setIsLoading(false); + setError(error); + setValue(null); } - } + }; React.useEffect(() => { if (autoRun) { - run() + run(); } - }, [autoRun]) + }, [autoRun]); return { value, error, isLoading, run, - } -} + }; +}; ``` ```jsx const App = () => { - const handleSubmit = (args) => { // args { foo: bar } - const url = "https://jsonplaceholder.typicode.com/todos" - return fetch(url).then(response => response.json()) - } + const handleSubmit = args => { + // args { foo: bar } + const url = "https://jsonplaceholder.typicode.com/todos"; + return fetch(url).then(response => response.json()); + }; - const submission = useAsync(handleSubmit, { autoRun: false }) + const submission = useAsync(handleSubmit, { autoRun: false }); return (
-
{JSON.stringify(submission.value, null, 2)}
- ) -} + ); +}; ``` From 6c7529775dd0cb973c6a92ef3c6c5dba8621881e Mon Sep 17 00:00:00 2001 From: Angelos Chalaris Date: Thu, 9 Jan 2020 20:22:12 +0200 Subject: [PATCH 7/7] Update and rename UseAsync.md to useAsync.md --- snippets/UseAsync.md | 73 -------------------------------------------- snippets/useAsync.md | 65 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 73 deletions(-) delete mode 100644 snippets/UseAsync.md create mode 100644 snippets/useAsync.md diff --git a/snippets/UseAsync.md b/snippets/UseAsync.md deleted file mode 100644 index 8c3ac1416..000000000 --- a/snippets/UseAsync.md +++ /dev/null @@ -1,73 +0,0 @@ ---- -title: useAsync -tags: hooks,state,effect,intermediate ---- - -A hook that handles asynchronous calls. - -- Create a custom hook that takes a handler `function` and `options`. -- Use the `React.useState()` hook to initialize the `value`, `error` and `loading` state variables. -- Use the `React.useEffect()` hook to call `run()` method and update the state variables accordingly if `options.autoRun` set to true. -- Use the `run` function to manually trigger `handler` function. -- Return an object containting the `value`, `error` and `isLoading` state variables and `run` function. - -```jsx -const useAsync = (fn, options = {}) => { - const [value, setValue] = React.useState(null); - const [error, setError] = React.useState(null); - const [isLoading, setIsLoading] = React.useState(false); - - const autoRun = options.autoRun || false; - - const run = async (args = null) => { - try { - setIsLoading(true); - const value = await fn(args); - setIsLoading(false); - setError(null); - setValue(value); - } catch (error) { - setIsLoading(false); - setError(error); - setValue(null); - } - }; - - React.useEffect(() => { - if (autoRun) { - run(); - } - }, [autoRun]); - - return { - value, - error, - isLoading, - run, - }; -}; -``` - -```jsx -const App = () => { - const handleSubmit = args => { - // args { foo: bar } - const url = "https://jsonplaceholder.typicode.com/todos"; - return fetch(url).then(response => response.json()); - }; - - const submission = useAsync(handleSubmit, { autoRun: false }); - - return ( -
- -
{JSON.stringify(submission.value, null, 2)}
-
- ); -}; -``` diff --git a/snippets/useAsync.md b/snippets/useAsync.md new file mode 100644 index 000000000..9881094f3 --- /dev/null +++ b/snippets/useAsync.md @@ -0,0 +1,65 @@ +--- +title: useAsync +tags: hooks,state,reducer,advanced +--- + +A hook that handles asynchronous calls. + +- Create a custom hook that takes a handler function, `fn`. +- Define a reducer function and an initial state for the custom hook's state. +- Use the `React.useReducer()` hook to initialize the `state` variable and the `dispatch` function. +- Define a `run` function that will run the provided callback, `fn`, while using `dispatch` to update `state` as necessary. +- Return an object containting the the properties of `state` (`value`, `error` and `loading`) and the `run` function. + +```jsx +const useAsync = (fn) => { + const initialState = { loading: false, error: null, value: null }; + const stateReducer = (_, action) => { + switch (action.type) { + case 'start': + return { loading: true, error: null, value: null}; + case 'finish': + return { loading: false, error: null, value: action.value}; + case 'error': + return { loading: false, error: action.error, value: null}; + } + } + + const [state, dispatch] = React.useReducer(stateReducer, initialState); + + const run = async (args = null) => { + try { + dispatch({ type: 'start' }); + const value = await fn(args); + dispatch({ type: 'finish', value }); + } catch (error) { + dispatch({ type: 'error', error }); + } + }; + + return { ...state, run }; +}; +``` + +```jsx +const RandomImage = props => { + const imgFetch = useAsync(url => fetch(url).then(response => response.json())); + + return ( +
+ +
+ { imgFetch.loading &&
Loading...
} + { imgFetch.error &&
Error { imgFetch.error }
} + { imgFetch.value && avatar} +
+ ); +}; + +ReactDOM.render(, document.getElementById('root')); +```