Waiting for dispatch in thunks - reactjs

I need to wait until dispatch in thunks is done. After that, I have to set state of hook to true.
Here is my service:
export const loadSearchInfoAsync = (product_id: string) => {
return (dispatch: Dispatch) => {
SearchService.getSearchInfo(product_id)
.then((response) => {
if (response) {
// Wait until this dispatch is done
dispatch(searchInfoLoadSuccess(response.data));
}
})
.catch((error) => {
if (error) {
dispatch(appErrorState(error.response));
}
});
};
};
And here is state which has to be updated after that dispatch
const handleScan = (data: string | null) => {
if (!proceed && data) {
// After this dispatch make setProceed true
dispatch(loadSearchInfoAsync(data));
setProceed(true);
}
};

Maybe it will help you
const loadSearchInfoAsync = (product_id: string, onSuccess, onFailure) => {
return (dispatch: Dispatch) => {
SearchService.getSearchInfo(product_id)
.then((response) => {
if (response) {
// Wait until this dispatch is done
dispatch(searchInfoLoadSuccess(response.data));
onSuccess()
}
})
.catch((error) => {
if (error) {
dispatch(appErrorState(error.response));
onFailure()
}
});
};
};
const loadSearchInfoPromise = (product_id: string) => {
return new Promise((resolve, reject) => {
dispatch(loadSearchInfoAsync(product_id, resolve, reject))
}
}
const handleScan = async (data: string | null) => {
if (!proceed && data) {
// After this dispatch make setProceed true
await loadSearchInfoPromise(data).then(() => {
setProceed(true);
})
}
};

I think in this case you could probably just move your proceed code into an effect and wait for a response on that?
useEffect(() => {
if (data.length) { // or do whatever check here to see if it's not empty
setProceed(true);
}
}, [data])

Related

Why my dispatch action doesn't work in use effect after request?

I need help. I don't understand why my dispatch action doesn't work. I've redux store currency list and current currency.
My reducer:
export const currencyReducer = (
state: typeState = initialState,
action: TypeActionCurrency
): typeState => {
switch (action.type) {
case types.CURRENCY_FILL_LIST:
return { ...state, list: action.payload }
case types.CURRENCY_SET_CURRENT:
return {
...state,
current:
state.list.find(currency => currency._id === action.payload) ||
({} as ICurrency),
}
default:
return state
}
}
My actions:
export const setCurrencyList = (currencies: ICurrency[]) => ({
type: types.CURRENCY_FILL_LIST,
payload: currencies,
})
export const setCurrentCurrency = (_id: string) => ({
type: types.CURRENCY_SET_CURRENT,
payload: _id,
})
My useEffect:
useEffect(() => {
if (!list.length) {
const fetchCurrencies = async () => {
try {
const data = await $apiClient<ICurrency[]>({ url: '/currencies' })
dispatch(setCurrencyList(data))
if (!current._id) dispatch(setCurrentCurrency(data[0]._id))
} catch (error) {
console.log(error)
}
}
fetchCurrencies()
}
}, [])
I want make request when load page and write currency list to Redux store, if we don't have current currency we write default currency from data.
There is one more strange thing, my redux extension shows that the state has changed, but when I receive it via the log or useSelector, it is empty
enter image description here
Thanks!
I am not 100% sure but it should work.
const [loader, setLoader] = useState(false);
const list = useSelector(state => state.list)
useEffect(() => {
if (!list.length) {
const fetchCurrencies = async () => {
try {
setLoader(true)
const data = await $apiClient<ICurrency[]>({ url: '/currencies' })
dispatch(setCurrencyList(data))
if (!current._id) dispatch(setCurrentCurrency(data[0]._id))
} catch (error) {
console.log(error)
} finally {
setLoader(false)
}
}
fetchCurrencies()
}
}, [])
useEffect(() => {
console.log(list);
}, [loader])

How can I wait until I get the dispatch result in React

consider the following code :
const onSubmit = (data) => {
dispatch(Actions.updatedUser(data))
navigation.navigate('xxxx')
}
When I call Submit function we want to wait until finishing dispatch then navigate , Here How can I do that ?
This is my action :
export const updatedUser = (model) => {
return dispatch => {
api
.patch("/xxx")
.then(response => { return response.data['data'] })
.then(result => {
dispatch({ type: Actions.AUTH_UPDATE_USER, payload: result })
})
.catch(error => { })
}
}
my reducer :
const initState = {
userInfo: undefined
}
export default (state = initState, action) => {
switch (action.type) {
case Actions.AUTH_UPDATE_USER:
return { ...state, userInfo: action.payload }
default:
return state;
}
}
Here's what you can do: from your action, you can return a promise which resolves only when the dispatch is completed. Something like this:
export const updatedUser = (model) => {
return dispatch => {
return new Promise((resolve, reject) => {
api
.patch("/xxx")
.then(response => { return response.data['data'] })
.then(result => {
dispatch({ type: Actions.AUTH_UPDATE_USER, payload: result })
resolve() // <<<< this!
})
.catch(error => { reject() })
})
}
}
Now, in your component code, you can either do .then or async/await based on your preference. Here's how it would look with then:
const onSubmit = (data) => {
dispatch(Actions.updatedUser(data)).then(() => {
navigation.navigate('xxxx')
})
}
Here's a sandbox for an example

How to test a dispatch function?

Hello I'm trying to test this function with the return of the dispatch in how many times it have been called, but really don't know how to do it correctly in order to call dispatch
export const mapDispatchToProps = (dispatch) => {
return {
hideSidebar: () => {
dispatch(hideSidebar)
},
updateUnit: (unitObject) => {
dispatch(settingsActions.updateArray(unitObject))
}
}
}
I have these test
describe('mapDispatchToProps', () => {
test('test', () => {
const dispatch = jest.fn(() => Promise.resolve())
mapDispatchToProps(dispatch)
expect(dispatch).toHaveBeenCalledTimes(2)
})
})
Any suggestions?
Create a dispatch mock function and pass it to mapDispatchToProps.
Then call the functions defined on the result.
You can use something like toHaveBeenCalledWith to verify that the correct action was dispatched:
// Stubs for hideSidebar and settingsActions.updateArray
const hideSidebar = { type: 'hide-side-bar' };
const settingsActions = { updateArray: u => ({ type: 'update-unit', payload: u })};
export const mapDispatchToProps = (dispatch) => {
return {
hideSidebar: () => {
dispatch(hideSidebar)
},
updateUnit: (unitObject) => {
dispatch(settingsActions.updateArray(unitObject))
}
}
}
test('mapDispatchToProps', () => {
const dispatch = jest.fn();
const map = mapDispatchToProps(dispatch);
map.hideSidebar();
expect(dispatch).toHaveBeenCalledWith({ type: 'hide-side-bar' }); // Success!
map.updateUnit({ theKey: 'theVal' });
expect(dispatch).toHaveBeenCalledWith({ type: 'update-unit', payload: { theKey: 'theVal' } }); // Success!
})

test mapDispatchToProps async actions

I am trying to test my mapDispatchToProps function when an asyncronous function is dispatched. I have read Dan Abramov's suggestions on how to test mapDispatchToProps and I am trying to test my code as such.
I am getting the error...
TypeError: Cannot read property 'then' of undefined
Here is my test...
describe("mapDispatchToProps", () => {
const dispatchSpy = jest.fn();
const {signupUser} = mapDispatchToProps(dispatchSpy);
it("should dispatch signupActions.signupUser()", () => {
// mockAxios.onPost(endpoints.SIGNUP,{})
// .reply(200,'test_success');
// tried with and without mockAxios
signupUser({})
const spyLastCall = dispatchSpy.mock.calls[0][0];
expect(spyLastCall)
.toEqual(signupActions.signupUser({}));
})
})
The function that I want to test...
export const mapDispatchToProps = dispatch => {
return { signupUser: (user) => {
dispatch(signupActions.signupUser(user))
.then((response) => {
// do something on success
}, (error) => {
// do something on failure
})
}
}
I have already tested signupActions.signupUser and I know that it returns a promise. Here is the code...
export function signupUser(user) {
return (dispatch) => {
return dispatch(rest.post(SIGNUP,user))
.then((response) => {
return Promise.resolve(response);
},(error) => {
return Promise.reject(error)
}
)
}}
What am I doing wrong?
Ps: I also tried:
const dispatchSpy = jest.fn().mockImplementation( () => {
return p = new Promise((reject,resolve) => {
resolve({})
})
}
with the same result
For anyone who is interested, I ended up using mergeProps which has made my tests a lot cleaner. Now I have...
export const mapDispatchToProps = dispatch => {
return { dispatchSignupUser: (user) => {
dispatch(signupActions.signupUser(user))
}
}
export const mergeProps = (propsFromState,propsFromDispatch,ownProps) => {
return {
signupUser: (values) => {
return propsFromDispatch.dispatchSignupUser(values)
.then(() => { // do something on success },
() => { // do something on failure})
}
}
and I test them separately...
describe("signup", () => {
/// ... ownProps and propsFromState declared here
const dispatchSpy = jest.fn((x) => {});
const {
dispatchSignupUser,
} = mapDispatchToProps(dispatchSpy);
const signupUser = mergeProps(propsFromState,propsFromDispatch,ownProps);
describe("mapDispatchToProps", () => {
it("should dispatch signup user on dispatchSignupUser", () => {
const spyOn = jest.spyOn(signupActions,'signupUser');
dispatchSignupUser({test: "test"});
expect(spyOn).toHaveBeenCalledWith({test: "test"});
})
})
describe("mergeProps", () => {
it("should do something on success", () => {
propsFromDispatch.dispatchSignupUser jest.fn().mockImplementation((x) => {
return new Promise((resolve,reject) => { return resolve({})} )
});
return signupUser({}).then(() => {
expect(history.location.pathname).toEqual("/signup/thank-you")
}, (error) => {})
})
})
})
Hopefully this is helpful!

Redux-thunk - dispatch is not a function

I'm having trouble with redux-thunk. It's saying dispatch is not a function inside my action creator, I tried consoling the returned arguments and there is none.
Here goes the code:
Action
export function signUp(data) {
return dispatch => {
console.log(dispatch)
if (data.email === 'email#server.com') {
dispatch(signIn(data, () => {
if (data.type === '2') {
browserHistory.push('/settings/profile')
} else {
browserHistory.push('/')
}
}))
} else {
return {
type: ActionTypes.USER_SIGN_UP__ERROR
}
}
}
}`
mapActionsToProps
const mapActionsToProps = dispatch => ({
signUp (data) {
console.log(dispatch)
dispatch(userActions.signUp(data))
}
})
By the way, you can see I consoled the dispatch function inside the mapActionsToProps, and it is returning as it was supposed to:
function (action) {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
}
Dispatch is not a function, because it's not passed from action creator.
Besides, you should not dispatch any action inside your mapActionsToProps. You just need to bind them to be accessible by connected component.
Your mapActionsToProps
const mapActionsToProps = (dispatch) => {
return {
asyncAction: bindActionCreators(asyncAction, dispatch),
}
}
const Container = connect(mapStateToProps, mapActionsToProps)(Component);
Async action
export const asyncAction = (email) => {
return (dispatch, getState) => {
const state = getState();
dispatch(StartAsync());
return fetch(`${apiUrl}/endpoint?email=${email}`, {
method: 'GET'
})
.then(response => response.json())
.then((result) => dispatch(finishedAsync(result)),
(error) => dispatch(failedAsync(error)))
.catch(e => {
console.log('error:', e);
});
};
};
Then, in your connected component, you can dispatch this action from props.

Resources