Our code currently has a function that calls a toast message popup while saving an object to the database.
export function addAnimal(animalObject) {
return function(dispatch) {
showToastNotification(() => {
return addAnimal(animalObject);
}, dispatch);
};
}
function showToastNotification(handleToastSave, dispatch) {
return handleToastSave()
.then(() => {
showMessage("this was a success")(dispatch);
})
.catch(() => {
showMessage("Something went wrong!")(dispatch);
})
.finally(() => {
saveMode(false)(dispatch);
});
}
My question is I want to pass a message string as a parameter to the showToastNotification like this:
export function addAnimal(animalObject) {
return function(dispatch) {
showToastNotification(("successfully added animal") => {
return addAnimal(animalObject);
}, dispatch);
};
}
function showToastNotification(message, handleToastSave, dispatch) {
return handleToastSave()
.then(() => {
showMessage(message)(dispatch);
})
.catch(() => {
showMessage("Something went wrong!")(dispatch);
})
.finally(() => {
saveMode(false)(dispatch);
});
}
This doesn't work. I'm not super familiar with how the fat arrow in this function works. Would it be possible to pass a prop to showToastNotification?
You're putting the string where the parameters of the callback would be, instead of as the first argument to the actual showToastNotification function. Just move it to the right place:
showToastNotification("successfully added animal", () => {
return addAnimal(animalObject);
}, dispatch);
Related
I have a websocket interface which I implemented so that I can use to send requests.
The problem is that the response is asynchronous and it initially returns the empty array because retObj is not updated from the callback function that I sent in. How can I make this function so that it will return the populated array when it has been updated.
This is how my Service looks like:
import * as interface from '../webSocket'
const carService = () => {
return {
getCars: () => {
interface.sendRequest(function (returnObject) {
//
}).then(d => d)
}
}
}
export default carService()
And this is how my action looks like:
import { GET_CARS } from '../constants'
import carService from '../carService'
export const getCars = () => async (dispatch) => {
try {
const cars = await carService.getCars()
console.log("At cars actions: ", cars) // logs: Array []
dispatch(getCarsSuccess(cars))
} catch (err) {
console.log('Error: ', err)
}
}
const getCarsSuccess = (cars) => ({
type: GET_CARS,
payload: cars
})
You simply have to wrap your callback into promise, since it was not a promise to begin with, which is why you cannot use then or await
import * as interface from '../webSocket'
const carService = () => {
return {
getCars: () => {
return new Promise(resolve => interface.sendRequest(function (returnObject) {
resolve(returnObject.msg)
}));
}
}
}
export default carService()
The problem is, you cant await a function unless it returns a Promise. So, as you can guess, the problem lies in carService.getCars's definition. Try this:
getCars: () => {
return new Promise((resolve, reject) => {
interface.sendRequest(function(returnObject) {
// if theres an error, reject(error)
resolve(returnObject);
})
})
}
Or, if sendRequest os am async function, simply return the return value of sendRequest:
getCars: () => {
return interface.sendRequest()
}
I am not sure why my function is giving me this error. In the function returning the value, I ensured there is a return statement on all levels. Could I have a second opinion as to why this error may be occurring? The service returns the data perfectly, the front end, however, is not seeing the return from my action.
I tried other solutions with the same title, so hopefully, this is not a duplicate
Front end:
componentDidMount() {
const { item } = this.props;
this.props.getDetails(Number)
.then((res) => {
this.setState({
data: res.response.response.Results
})
});
}
mapping:
function mapDispatchToProps(dispatch) {
return {
getDetails(Number) {
dispatch(getActions.getDetails(Number));
}
};
}
function retrieving:
function getDetails(Number) {
return (dispatch) => {
dispatch({ type: policyConstants.ITEM_GETDETAIL_REQUEST });
return Service.getDetails(Number)
.then(
response => {
dispatch({ type: Constants.ITEM_GETDETAIL_SUCCESS });
var pDetails = response.response.Results;
return { pDetails };
},
error => {
dispatch({ type: policyConstants.POLICY_GETDETAIL_FAILURE, error});
}
);
}
}
this.props.getDetails(Number) in your componentDidMount is evaluated to undefined because getDetails in your mapDispatchToProps returns nothing.
Change like this :
function mapDispatchToProps(dispatch) {
return {
getDetails(Number) {
return dispatch(getActions.getDetails(Number));
}
};
}
I have this React-Native app, which fetches a list of items, and then, an image gallery for each of those items. So basically, I have two ajax function, and the second one needs the list of items fetched in the first function.
I make the first invoke in componentDidMount(), but I don't know how to "wait" for it to finish to make the second call. If I just place them one after the other, the second one won't do anything because there is no list of items yet.
How can I solve this? I read similar questions here on StackOverflow but couldn't find a clear example for this case. Also, in the code, I commented some of my unsuccessful tries.
Thanks in advance, I'm new to React so I can be missing something simple.
component.js
class EtiquetasList extends Component {
componentDidMount() {
this.props.FetchEtiquetas();
if ( this.props.etiquetas.length > 0 ) { // I tried this but didn't do anything
this.props.FetchGalleries( this.props.etiquetas );
}
}
renderEtiquetas() {
if ( this.props.galleries.length > 0 ) {
// if I invoke FetchGalleries here, it works, but loops indefinitely
return this.props.etiquetas.map(etiqueta =>
<EtiquetaDetail key={etiqueta.id} etiqueta={etiqueta} galleries={ this.props.galleries } />
);
}
}
render() {
return (
<ScrollView>
{ this.renderEtiquetas() }
</ScrollView>
);
}
}
const mapStateToProps = (state) => {
return {
etiquetas: state.data.etiquetas,
isMounted: state.data.isMounted,
galleries: state.slides.entries
};
};
export default connect(mapStateToProps, { FetchEtiquetas, FetchGalleries })(EtiquetasList);
actions.js
export function FetchEtiquetas() {
return function (dispatch) {
axios.get( url )
.then(response => {
dispatch({ type: FETCH_ETIQUETAS_SUCCESS, payload: response.data })
}
);
}
}
export function FetchGalleries( etiquetas ) {
return function (dispatch) {
return Promise.all(
etiquetas.map( record =>
axios.get('https://e.dgyd.com.ar/wp-json/wp/v2/media?_embed&parent='+record.id)
)).then(galleries => {
let curated_data = [];
let data_json = '';
galleries.map( record => {
record.data.map( subrecord => {
// this is simplified for this example, it works as intended
data_json = data_json + '{ title: "' + subrecord.title+'"}';
});
my_data.push( data_json );
});
return dispatch({ type: FETCH_GALLERIES_SUCCESS, payload: curated_data });
});
}
}
reducer.js
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case FETCH_ETIQUETAS_SUCCESS:
return { ...state, etiquetas: action.payload, isMounted: true }; // I think it ran faster like this tho
default:
return state;
}
};
You may try modifying your "Fetch" functions to return the Promise object. Then you can chain them togather like this:
export function FetchEtiquetas() {
return function (dispatch) {
return axios.get( url ).then(response => {
dispatch({ type: FETCH_ETIQUETAS_SUCCESS, payload: response.data })
})
}
}
Invoke in "componentDidMount":
this.props.FetchEtiquetas().then(() => {
this.props.FetchGalleries( this.props.etiquetas );
})
componentWillReceiveProps(nextProps) {
if(nextProps.etiquetas.length !== 0 && this.props.etiquetas.length === 0) {
this.props.FetchGalleries( nextProps.etiquetas );
}
}
I think componentWillReceiveProps lifecycle can do the job easily. You should use componentWillReceiveProps whenever you need to trigger something in update phase of react components
To chain your actions, I would recommend chaining Promise or using async/await if you can.
To do it with Promises, you should return the Promise returned by the request in the action.
export function FetchEtiquetas() {
return function (dispatch) {
return axios.get( url )
.then(response => {
dispatch({ type: FETCH_ETIQUETAS_SUCCESS, payload: response.data })
}
);
}
}
And use mapDispatchToProps on redux connect call, it should look like
function mapDispatchToProps(dispatch) {
return {
fetchData: () => {
dispatch(FetchEtiquetas)
.then((results) => {
dispatch(FetchGalleries(results))
});
}
}
}
Then in your component, just call the right prop in componentDidMount
componentDidMount() {
this.props.fetchData();
}
From your code I assume you are using redux-thunk.
I suggest combining them together to an action creator like this (which is possible in redux-thunk):
export function FetchEtiquetasThenGalleries() {
return function (dispatch) {
axios.get(url)
.then(response => {
dispatch({ type: FETCH_ETIQUETAS_SUCCESS, payload: response.data })
const etiquetas = response.data; // Assuming response.data is that array.
return Promise.all(
etiquetas.map(record =>
axios.get('https://e.dgyd.com.ar/wp-json/wp/v2/media?_embed&parent=' + record.id)
)).then(galleries => {
let curated_data = [];
let data_json = '';
galleries.map(record => {
record.data.map(subrecord => {
// this is simplified for this example, it works as intended
data_json = data_json + '{ title: "' + subrecord.title + '"}';
});
my_data.push(data_json);
});
dispatch({ type: FETCH_GALLERIES_SUCCESS, payload: curated_data });
});
});
}
}
In my react component I am calling an action in ComponentDidMount() like this:
componentDidMount() {
const { actions } = this.props
function save_project_continiously() {
console.log("inside")
actions.save_project().then(save_project_continiously)
}
save_project_continiously()
}
Here I am calling an action continuously.. My actions is like:
export function save_project() {
return (dispatch, getState) => {
setTimeout(() => {
return dispatch(manage_data()).then(response => {
console.log("Hellooww world")
return response
})
}, 3000)
}
}
When I do this it gives me error saying .then() is not a function in ComponentDidMount() ..
If I do
export function save_project() {
return (dispatch, getState) => {
return dispatch(manage_data()).then(response => {
console.log("Hellooww world")
return response
})
}
}
It is called continuously but I want it to be called continuously after specific time.
I tried this:
export function save_project() {
return (dispatch, getState) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
return dispatch(manage_data()).then(response => {
console.log("Hellooww world")
return response
})
}, 3000)
})
}
}
But it is only called once.. Does not give any error but it only calls once..
What I want is I want to call an actions continuosly after specific time after completing the action.
Here I want to call save_project and after completing it I again want to call it after 3 seconds and go on continuously ..
How can I make it happen ??
Any suggestions ??
Thanks in advance
In this code, you're wrapping promise in promise, but what you actually want to do is execute this code again on successful promise resolve.
export function save_project() {
// So this code does return callback, so it does not return promise
// in your first example.
return (dispatch, getState) => {
// Then this code returns promise, but it never gets resolved.
return new Promise((resolve, reject) => {
setTimeout(() => {
// Then you return promise from set timeout, but it
// is doubtful as well
return dispatch(manage_data()).then(response => {
console.log("Hellooww world")
return response
})
}, 3000)
})
}
}
What you really want to do is:
// Just return promise of dispatch instead of wrapping it in
// a callback.
export function save_project() {
return dispatch(manage_data());
}
// Then use set timeout here
function save_project_continiously() {
console.log("inside")
actions.save_project().then(() => {
setTimeout(save_project_continiously, 3000);
});
}
Or if you really want a callback in save_project, you need to call it properly with proper args as in your example they're undefined anyway.
Try setInterval()
export function save_project() {
return new Promise((resolve, reject) => {
setTimeout(() => {
dispatch(manage_data()).then(response => {
resolve(response);
}, error => {
reject(response);
})
}, 3000)
});
}
Sorry for my English, I hope you can understand me.
I use redux-thunk for async actions.
// actionA.js
export function actionA() {
return fetch('api call')
.then((data) => ({
type: 'actionA',
data: data
}))
}
// reducer.js
export function reducer(state, action) {
...
if(action.type === 'actionA') {
return {
...,
dataA: action.data,
...
};
}
}
// actionB.js
export function actionB() {
return (dispatch, getState) => {
if(!getState().dataA) {
dispatch(actionA());
}
doSomethingWithDataA(getState().dataA);
};
}
In some cases, I only need to dispatch actionA without dispatching actionB. But when I dispatching actionB, actionB will use dataA, and dataA is created by actionA. So in actionB, I will check if there is dataA in Store, if not, I will dispatch actionA first. But actionA is an async action, I can not get when actionA is done.
So how to deal with this problem?
Generally speaking, you want to do something when dataA is ready and actionB had happened. You can do it using middleware
function myMiddleware() {
return store => next => action => {
const dataA = store.getState().dataA;
if(store.getState().dataA && action.type === 'actionB') {
doSomethingWithDataA(dataA);
}
};
}
Then your actionB turns into the following
function lazyFetchDataA() {
return (dispatch, getState) => {
if(!getState().dataA) {
return dispatch(actionA());
} else {
return Promise.resolve();
}
}
}
export function actionB() {
return (dispatch, getState) => {
lazyFetchDataA().then(() => dispatch({type: 'actionB'}))
}
}
It looks difficult, but now you have little pieces of code in proper places that do your thing.
Probably you don't have to do such a difficult thing if you provide more information what your doSomethingWithDataA is, I would give a better answer.
This approach might help, note I will be using the fetch polyfill to return a promise for aysnc request.
function actionA(dataA) {
return {
type: 'actionA',
data: dataA
};
}
export function actionB() {
return (dispatch, getState) => {
if(!getState().dataA) {
return fetch('api call')
.then((data) => {
dispatch(actionA(data))
})
.catch((error) => {// handle error})
}
return doSomethingWithDataA(getState().dataA);
}
}