With the version you would navigate away immediately, before the response has arrived. You should at least get a warning that you are trying to set a state on an unmounted component.
- I've just tried it, and - as expected - with your version the new item is not added to the "All Quotes" view (until that component is mounted again by some other action).
Not a good Practice NewQuote.js
import React, { useEffect } from 'react'
import QuoteForm from '../components/quotes/QuoteForm';
import { useHistory } from 'react-router-dom';
import useHttp from '../hooks/use-http';
import { addQuote } from '../lib/api';
function NewQuote() {
const { sendRequest, status } = useHttp(addQuote);
const history = useHistory();
const addQuoteHandler = (quoteData) => {
sendRequest(quoteData);
history.push('/quotes');
}
return (
<QuoteForm isLoading={status === 'pending'} onAddQuote={addQuoteHandler} />
)
}
export default NewQuote
Best Practice : That's how we handle the side effect , because sendRequest is an async function and of course there is a delay here . After sending the request , the response is not getting directly , so before any error announcement or something like that we can use the useEffect hook.
Best Practice NewQuote.js
import React, { useEffect } from 'react'
import QuoteForm from '../components/quotes/QuoteForm';
import { useHistory } from 'react-router-dom';
import useHttp from '../hooks/use-http';
import { addQuote } from '../lib/api';
function NewQuote() {
const { sendRequest, status } = useHttp(addQuote);
const history = useHistory();
useEffect(() => {
if (status === 'completed') {
history.push('/quotes');
}
}, [status, history]);
const addQuoteHandler = (quoteData) => {
sendRequest(quoteData);
}
return (
<QuoteForm isLoading={status === 'pending'} onAddQuote={addQuoteHandler} />
)
}
export default NewQuote
Instead of useEffect you could also write it like this:
const addQuoteHandler = (quoteData) => {
sendRequest(quoteData).then(() => history.push('/quotes'));
};
... or ...
const addQuoteHandler = async (quoteData) => {
await sendRequest(quoteData);
history.push('/quotes');
};
api.js:
export async function addQuote(quoteData) {
const response = await fetch(`${FIREBASE_DOMAIN}/quotes.json`, {
method: 'POST',
body: JSON.stringify(quoteData),
headers: {
'Content-Type': 'application/json',
},
});
const data = await response.json();
// console.log(data); { name: '-NAob1i5CCgXVtgo-GCq' }
if (!response.ok) {
throw new Error(data.message || 'Could not create quote.');
}
return null;
}
Custom Hook use-http.js
import { useReducer, useCallback } from 'react';
function httpReducer(state, action) {
if (action.type === 'SEND') {
return {
data: null,
error: null,
status: 'pending',
};
}
if (action.type === 'SUCCESS') {
return {
data: action.responseData,
error: null,
status: 'completed',
};
}
if (action.type === 'ERROR') {
return {
data: null,
error: action.errorMessage,
status: 'completed',
};
}
return state;
}
function useHttp(requestFunction, startWithPending = false) {
const [httpState, dispatch] = useReducer(httpReducer, {
status: startWithPending ? 'pending' : null,
data: null,
error: null,
});
const sendRequest = useCallback(
async function (requestData) {
dispatch({ type: 'SEND' });
try {
const responseData = await requestFunction(requestData);
// console.log(responseData);
dispatch({ type: 'SUCCESS', responseData });
} catch (error) {
dispatch({
type: 'ERROR',
errorMessage: error.message || 'Something went wrong!',
});
}
},
[requestFunction]
);
return {
sendRequest,
...httpState,
};
}
export default useHttp;