// how can I use the promise of toastify like I want to show spinner while fetching data then message success or failed
// but I am getting error in bellow code
const fetch = () => {
axios
.get("https://restcountries.com/v2/name/india")
.then((res) => {
toast.promise({
pending:"pending",
success:"success",
error:"rejected"
} )
console.log(res);
})
.catch((err) => {
toast.error("🦄 failed", {
position: "top-center",
autoClose: 2000,
hideProgressBar: true,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined
});
});
};
According to toast API https://fkhadra.github.io/react-toastify/promise/ the syntax should be
const myPromise = fetchData();
toast.promise(myPromise, {
loading: 'Loading',
success: 'Got the data',
error: 'Error when fetching',
})
An example which can be found on https://codesandbox.io/s/twilight-bash-jzs24y?file=/src/App.js
export default function App() {
const myPromise = new Promise((resolve) =>
fetch("https://jsonplaceholder.typicode.com/todos/1")
.then((response) => response.json())
.then((json) => setTimeout(() => resolve(json), 3000))
// setTimeout just for the example , cause it will load quickly without it .
);
useEffect(() => {
toast.promise(myPromise, {
pending: "Promise is pending",
success: "Promise Loaded",
error: "error"
});
}, []);
return (
<div className="App">
<ToastContainer />
</div>
);
}
If you are not using promise. Use toast.loading.
(DOCS: https://fkhadra.github.io/react-toastify/promise/#toastloading)
const getData = () => {
const id = toast.loading("Please wait...")
axios.get(`some-url`)
.then(res => {
toast.update(id, {render: "All is good", type: "success", isLoading: false});
}).catch(err => {
toast.update(id, {render: "Something went wrong", type: "error", isLoading: false });
});
}
If it is not working then store toast id in useRef and then it will work.
You can use toast.update (https://fkhadra.github.io/react-toastify/update-toast)
const toastId = useRef(null)
const fetch() => {
toastId.current = toast.loading("Loading...")
axios
.post(...)
.then(() => {
toast.update(toastId.current, {
render: "Your message...",
type: "success",
isLoading: "false"
}
})
.catch(() => {
toast.update(toastId.current, {
render: "Your message...",
type: "error",
isLoading: "false"
}
})
}
Related
I am working on my test for a new project and have them set up but am running into an async issue with jest.
I start with jest.mock('axios');
And the working test is
it('Dispatches SNACK_BAR after submitting ForgotPassword', async () => {
let store = configureStore({ reducer: {
auth: authReducer,
snackBar: snackBarReducer
}})
axios.post.mockResolvedValueOnce({headers: {
authorization: 'asdasdasdas'
},
status: 200});
await store.dispatch(forgotPasswordActions.forgotPasswordPost('test#test.com', (path) => {}))
expect(store.getState().snackBar).toEqual({"message": "Check your email for a reset link", "severity": "success", "timestamp": store.getState().snackBar.timestamp});
});
But when I try the fail case
it('Dispatches SNACK_BAR after submitting ForgotPassword with an error', async () => {
let store = configureStore({ reducer: {
auth: authReducer,
snackBar: snackBarReducer
}})
axios.post.mockRejectedValueOnce({response: {headers: {
authorization: 'asdasdasdas'
},
status: 500,
data: {
error: 'Error'
}}});
await store.dispatch(forgotPasswordActions.forgotPasswordPost('test#test.com', (path) => {}))
expect(store.getState().snackBar).toEqual({"message": "Error", "severity": "error"});
})
The expect doesn't wait for the dispatch to resolve. If I change the mockRejectedValueOnce to mockResolvedValueOnce then I get a similar result to the first test. It seems the only difference is mockRejectedValueOnce but I am not sure why
export const forgotPasswordPost = ({ email }, navigate) => async dispatch => {
axios.post(`${ROOT_URL}/auth/password`,
{ "user": { email: email }})
.then(response => {
return dispatch({ type: SNACK_BAR, payload: ["Check your email for a reset link", "success"]})
})
.catch(response => {
return dispatch({ type: SNACK_BAR, payload: ["Error", "error"]});
})
}
The function that is being tested
In forgotPasswordPost you return async function async dispatch => {}. This function has to return a promise. But currently it is void
/// original function
export const forgotPasswordPost = ({ email }, navigate) => async dispatch => {
axios.post(`${ROOT_URL}/auth/password`,
{ "user": { email: email }})
.then(response => {
return dispatch({ type: SNACK_BAR, payload: ["Check your email for a reset link", "success"]})
})
.catch(response => {
return dispatch({ type: SNACK_BAR, payload: ["Error", "error"]});
})
}
/// Try to to return THE Promise, not you return the axios.post promise chain
export const forgotPasswordPost = ({ email }, navigate) => async dispatch => {
return axios.post(`${ROOT_URL}/auth/password`,
{ "user": { email: email }})
.then(response => {
return dispatch({ type: SNACK_BAR, payload: ["Check your email for a reset link", "success"]})
})
.catch(response => {
return dispatch({ type: SNACK_BAR, payload: ["Error", "error"]});
})
}
/// ANother proposal using async await
export const forgotPasswordPost = ({ email }, navigate) => async dispatch => {
try {
const response = await axios.post(`${ROOT_URL}/auth/password`, { "user": { email: email }});
dispatch({ type: SNACK_BAR, payload: ["Check your email for a reset link", "success"]})
} catch (e) {
dispatch({ type: SNACK_BAR, payload: ["Error", "error"]});
}
}
Using react.js & firebase
The code below represents a simple button which increases/decreases +1/-1 whenever its clicked. It also updates one of the documents on the backend (using firebase). Everything seems to work fine on the surface but not on firebase. When you click on the button, it'll show +1 on the UI and console.log but not on firebase. In other words when plusCount state is at 0, it shows +1 on firebase and when plusCount state is at +1, it shows 0 on firebase. How can I fix this to make sure it shows the same number on the frontend and the backend? I also added the useFirestore hook component below, there may be a mistake that I'm unaware of in there somewhere.
Thank you for any help.
Button component:
import React, { useState } from 'react';
import { useFirestore } from "../../hooks/useFirestore"
export default function Testing({ doc }) {
const { updateDocument } = useFirestore('projects')
const [plusActive, setPlusActive] = useState(false)
const [plusCount, setPlusCount] = useState(0)
function p() {
setPlusActive(prevState => !prevState);
plusActive ? setPlusCount(plusCount - 1) : setPlusCount(plusCount + 1)
}
const handlePlus = (e) => {
e.preventDefault();
p();
updateDocument(doc.id, {
votes: plusCount
})
}
console.log(plusCount)
return (
<div>
<button onClick={handlePlus}>like | {plusCount}</button>
</div>
)
}
useFirestore hook component:
import { projectFirestore, timestamp } from "../firebase/config"
let initialState = {
document: null,
isPending: false,
error: null,
success: null,
}
const firestoreReducer = (state, action) => {
switch (action.type) {
case 'IS_PENDING':
return { isPending: true, document: null, success: false, error: null }
case 'ADDED_DOCUMENT':
return { isPending: false, document: action.payload, success: true, error: null }
case 'DELETED_DOCUMENT':
return { isPending: false, document: null, success: true, error: null }
case 'ERROR':
return { isPending: false, document: null, success: false, error: action.payload }
case "UPDATED_DOCUMENT":
return { isPending: false, document: action.payload, success: true, error: null }
default:
return state
}
}
export const useFirestore = (collection) => {
const [response, dispatch] = useReducer(firestoreReducer, initialState)
const [isCancelled, setIsCancelled] = useState(false)
// collection ref
const ref = projectFirestore.collection(collection)
// only dispatch if not cancelled
const dispatchIfNotCancelled = (action) => {
if (!isCancelled) {
dispatch(action)
}
}
// add a document
const addDocument = async (doc) => {
dispatch({ type: 'IS_PENDING' })
try {
const createdAt = timestamp.fromDate(new Date())
const addedDocument = await ref.add({ ...doc, createdAt })
dispatchIfNotCancelled({ type: 'ADDED_DOCUMENT', payload: addedDocument })
}
catch (err) {
dispatchIfNotCancelled({ type: 'ERROR', payload: err.message })
}
}
// delete a document
const deleteDocument = async (id) => {
dispatch({ type: 'IS_PENDING' })
try {
await ref.doc(id).delete()
dispatchIfNotCancelled({ type: 'DELETED_DOCUMENT' })
}
catch (err) {
dispatchIfNotCancelled({ type: 'ERROR', payload: 'could not delete' })
}
}
// update a document
const updateDocument = async (id, updates) => {
dispatch({ type: "IS_PENDING" })
try {
const updatedDocument = await ref.doc(id).update(updates)
dispatchIfNotCancelled({ type: "UPDATED_DOCUMENT", payload: updatedDocument })
return updatedDocument
}
catch (error) {
dispatchIfNotCancelled({ type: "ERROR", payload: error })
return null
}
}
useEffect(() => {
return () => setIsCancelled(true)
}, [])
return { addDocument, deleteDocument, updateDocument, response }
}```
For your use-case, you should useEffect() to listen the changes for plusCount. See code below:
useEffect(() => {
updateDocument('test', {
votes: plusCount
})
}, [plusCount]);
const handlePlus = (e) => {
e.preventDefault();
setPlusActive(prevState => !prevState);
plusActive ? setPlusCount(plusCount - 1) : setPlusCount(plusCount + 1)
}
Everytime you click the button it will listen to the changes of plusCount which then the updateDocument will also be triggered together with the updated state. See below screenshot for the result:
As you can see, the frontend and backend is now aligned.
You can find more information by checking out this documentation.
I'm learning react by building a weather api. I make an API call and store it in state.
state = {
forecasts: {
error: null,
isLoaded: false,
forecasts: []
}
}
componentDidMount() {
const endpoint = `http://dataservice.accuweather.com/forecasts/v1/daily/5day/207931?apikey=KEY&language=en&details=true&metric=true`;
fetch(endpoint)
.then(res => res.json())
.then((result) => {
this.setState({
'forecasts.isLoaded': true,
'forecasts.forecasts': result.DailyForecasts,
});
},
(error) => {
this.setState({
'forecasts.isLoaded': true,
'forecasts.error': error
});
})
}
When I pass this down as props, I get no data?
<WeatherOverview weather={this.state.forecasts}/>
Use spread syntax to copy the entire previous object and then override some of its keys. You should also use the form of setState that takes a function because you want to reference the previous value of state.forecasts:
.then((result) => {
this.setState(state => ({
forecasts: {
...state.forecasts,
isLoaded: true,
forecasts: result.DailyForecasts,
},
}));
},
(error) => {
this.setState(state => ({
forecasts: {
...state.forecasts,
isLoaded: true,
error: error,
},
}));
})
or you may want entirely new objects to wipe out the previous error state:
.then((result) => {
this.setState({
forecasts: {
error: null,
isLoaded: true,
forecasts: result.DailyForecasts,
},
});
},
(error) => {
this.setState(state => ({
forecasts: {
forecasts: [],
isLoaded: true,
error: error,
},
}));
})
you are not passing the state correctly, you need to pass the state without quotation marks
this.setState({
'forecasts.isLoaded': true,
'forecasts.forecasts': result.DailyForecasts,
});
should be like this:
this.setState({
forecasts: {
...state.forecasts,
isLoaded:true,
forecasts:result.DailyForecasts},
});
How do I pass an ID from one API to another, and fetch the require data?
This is my code:
handleClick(e){
fetch("http://api.com/product_sub_categories?category_id")
.then(res => res.json())
.then(
(result) => {
this.setState({
isLoaded: true,
product_sub_categories: result.product_sub_categories
});
},
(error) => {
this.setState({
isLoaded: true,
error
});
}
)
}
You can do using back tick.
handleClick(e){
fetch(`${BASE_PATH}/product_sub_categories?category_id=${e.target.value}`)
.then(res => res.json())
.then(
(result) => {
this.setState({
isLoaded: true,
product_sub_categories: result.product_sub_categories
});
},
(error) => {
this.setState({
isLoaded: true,
error
});
}
)
}
I have action that always returns Promise.reject:
module.exports = { create: createActionAsync('CREATE_USER', () => {
return Promise.reject({
response: {
type: 'error',
message: 'It will be implemented soon',
},
});
})}
But in component catch block doesn't work:
onAddUser(data) {
const { userActions: { create } = {} } = this.props;
create(data)
.then(() => {})
.catch(err => console.error(err)) // not working