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

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

Related

Waiting for dispatch in thunks

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])

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])

redux handling fetch response data and update store

I'm trying to migrate to redux-thunk I have few issues here.
Previously, I had fetch response data which is saved in state, and with that state I used filter to save other states in the app.
loadMeetingRoomData = async () => {
try {
const getMeetingRoomData = await fetch(URL, { headers: myHeaders });
const responseJson = await getMeetingRoomData.json();
if (responseJson.length === 0) {
this.setState({
meetingRoomStatus: false,
})
} else {
this.setState({
meetingRoomData: responseJson,
});
const inProgressMeeting = responseJson.filter(obj => {
return obj.Status === INPROGRESS_CODE
});
this.setState({
meetingRoomInProgressCount: inProgressMeeting.length,
});
const upcomingMeeting = responseJson.filter(obj => {
return obj.Status === UPCOMING_CODE_MORE_THAN_30_MIN || obj.Status === UPCOMING_CODE_LESS_THAN_30_MIN;
});
this.setState({
meetingRoomUpcomingCount: upcomingMeeting.length,
})
const finishedMeeting = responseJson.filter(obj => {
return obj.Status === FINISHED_CODE
})
this.setState({
meetingRoomFinishedCount: finishedMeeting.length,
})
}
}
catch (err) {
console.log(new Error(err));
}
}
And below is the code, My question is where do I filter the reponseJson to
update the new three states in the previous code ( meetingRoomInProgressCount, meetingRoomUpComingCount, meetingRoomFinished)
Do I have to dispatch it in the component file using mapDispatchToProps or
use middleware to update the store , or filter inside the reducer?
#Action Creator
export const fetchMeetingRoom = () => {
return function(dispatch){
return fetch(URL, { headers: myHeaders })
.then(response => response.json())
.then(json =>
dispatch({ type: 'FETCH_MEETINGROOM_SUCCESS' ,payload : json})
)
}
}
export default function reducer( state = initialState , action){
switch(action.type) {
case FETCH_MEETINGROOM_SUCCESS :
let value = action.payload;
if(value.length === 0){ return {...state,meetingRoomStatus :false,}}
else{
return {...state,
isLoading : false,
meetingRoomData : action.payload}
}
default :
return state;
}
}
I use this structure for my projects
actions:
import { TYPE_VARS } from "./type";
export const func_name = () => (dispatch) => {
return fetch(URL, {
method: "GET",
headers: {
"Content-Type": "application/json",
}
})
.then((res) => res.json())
.then((data) => {
dispatch({
type: TYPE_VARS,
payload: data,
});
return data;
})
.catch((err) => console.log(err.message));
};
reducer:
import { TYPE_VARS } from "../actions/type";
const initialState = {
my_state: {},
};
export default function (state = initialState, action) {
const { type, payload } = action;
switch (type) {
case TYPE_VARS:
return {
...state,
my_state: payload,
};
default:
return state;
}
}
component:
// call action where do you need
func_name()
const mapStateToProps = state => ({
my_state: state.reducerFileName.my_state
})
export default connect(mapStateToProps, { func_name })(ComponentName)

React Redux -possible to have a call back in dispatch function

Guys i am having some trouble or quite doubtful.
am having one component and one reducer.
Reducer.js
import {
ASSET_POPUP_GET_ENDPOINT,
} from 'apiCollection';
import { performGet } from 'services/rest-service/rest-service';
export const GET_ASSETS_LIST = 'stories/GET_ASSETS_LIST';
const initialState = {
imgGroup: [],
isLoading: false,
};
const modalUploadReducer = (state = initialState, action) => {
switch (action.type) {
case GET_ASSETS_LIST: {
return {
...state,
ImageJson:action.payload.imageGroup,
};
}
case GET_ASSETS_LIST_ERROR: {
return {
...state,
isLoading:false,
};
}
default:
return state;
}
};
export const getModalClose = () => (dispatch) => {
dispatch({ type: CLOSE_MODAL });
}
export const getListActionDispactcher = () => (dispatch) => {
performGet(`${ASSET_POPUP_GET_ENDPOINT}`)
.then((response) => {
const payload = response.data;
dispatch({ type: GET_ASSETS_LIST,
payload: {
...payload,
data: payload.results,
} });
})
.catch((err) => {
dispatch({ type: GET_ASSETS_LIST_ERROR, payload: err });
throw err;
});
};
export default modalUploadReducer;
and my component look like
it do have mapStateToProps and mapDispatchToProps
and one of the function
const mapDispatchToProps = dispatch => ({
getCollection: () => dispatch(getListActionDispactcher()),
});
addDocumentClick = () =>{
this.props.getAssetsCollection();
}
and is it possible to have some setState/manipulation of response after api response got from reducer in the component
based on the response i need to do some changes in addDocumentClick.
Means something like this
addDocumentClick = () =>{
this.props.getAssetsCollection().then(...based on response;
}
The correct way for solving this is setting a global loading flag and in your componentDidUpdate() method, checking for the value to determine that the action has just succeeded. You already seem to have the isLoading flag. Just set it when the action's dispatched, and unset it after it succeeds/fails. And in componentDidUpdate():
function componentDidUpdate(prevProps) {
if (prevProps.isLoading && !this.props.isLoading) {
// do something
}
}
Of course, you need to connect() your loading flag to your component to achieve this.
If all you care about is whether the assets list has changed, you can simply check for the change of that prop in componentDidUpdate():
function componentDidUpdate(prevProps) {
if (prevProps.ImageJson !== this.props.ImageJson) {
// do something
}
}
Another solution is sending a callback to your action dispatcher, which makes your code more tightly coupled and I don't recommend, but it does work too. So, when you connect(), you can:
getCollection: (onSuccess) => dispatch(getListActionDispactcher(onSuccess)),
In your action dispatcher:
export const getListActionDispactcher = (onSuccess) => (dispatch) => {
// ...once API finished/failed
onSuccess(someData);
}
Finally, in your component:
this.props.getCollection((result) => {
console.log('succeeded!', result);
// hide modal, etc..
}
You are using redux-thunk, and calling thunk will return a promise which will resolve in whatever you return in your thunk. Therefore, all you need to do is to add return value to getListActionDispactcher
export const getListActionDispactcher = () => (dispatch) => {
// return this promise
return performGet(`${ASSET_POPUP_GET_ENDPOINT}`)
.then((response) => {
const payload = response.data;
dispatch({ type: GET_ASSETS_LIST,
payload: {
...payload,
data: payload.results,
} });
// return whatever you want from promise
return payload
})
.catch((err) => {
dispatch({ type: GET_ASSETS_LIST_ERROR, payload: err });
throw err;
});
};
.
addDocumentClick = () => {
this.props.getAssetsCollection().then(payload => console.log(payload))
}
You should, however, look for ways to avoid this pattern to have your components decoupled from actions as much as possible for the sake of modularity

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