return (dispatch) do nothing - javascript

i have a component which run this
import { handleAddQuestion } from '../actions/questions';
handleAddQuestion(optionOneText, optionTwoText, author)
in actions/questions i have
import { _saveQuestion } from '../utils/_DATA';
export function handleAddQuestion (optionOneText, optionTwoText, authedUser) {
console.log("before")
return (dispatch) => {
console.log("after")
_saveQuestion({optionOneText, optionTwoText, author: authedUser}).then((question) => {
let qid = question.id
dispatch(saveUserQuestion(authedUser, qid))
dispatch(addQuestion(question))
})
}
}
i get before in console but not after no action is triggered, nothing happens at all no matter what i change ,, it's like i cannot do this return (dispatch) !! i tried the same thing in another place which worked perfectlly, logged the data and triggered the actions ! what is wrong here !!!!!!!
more info _DATA_.js
export function _saveQuestion (question) {
return new Promise((res, rej) => {
const authedUser = question.author;
const formattedQuestion = formatQuestion(question);
setTimeout(() => {
questions = {
...questions,
[formattedQuestion.id]: formattedQuestion
}
users = {
...users,
[authedUser]: {
...users[authedUser],
questions: users[authedUser].questions.concat([formattedQuestion.id])
}
}
res(formattedQuestion)
}, 1000)
})
}
reducers/questions.js
export function questions(state = {}, action) {
switch (action.type) {
case ADD_QUESTION:
const { question } = action;
return {
...state,
[question.id]: question,
};
///
any help ?

You are returning what is called a thunk here - and thunks only get executed when actually being dispatched. Compare that with how it is called in the other places.
So correct would be calling
dispatch(handleAddQuestion(optionOneText, optionTwoText, author))

Related

Redux - Asynchronous response from web socket request

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()
}

Javascript - Redux actions don't run consecutively

I have a situation when I need 2 Redux Actions to be run consecutively.
The context is a user clicks on a Preview button, and I want to display a loader until the puzzle is done generating.
function mapDispatchToProps(dispatch) {
return {
onPreview: () => {
dispatch(generatePreview());
},
};
}
In order to do it, I use the middleware redux-thunk and the action I want to be executed first returns a Promise.resolve() and my second action is in the then():
export function generatingPreview() {
return dispatch => {
dispatch({
type: GENERATING_PREVIEW,
});
return Promise.resolve();
};
}
export function generatePreview() {
return (dispatch, getState) => {
dispatch(generatingPreview()).then(() => {
const state = getState();
const conf = state.getIn(['login', 'conf']).toJS();
const wordList = state.getIn(['login', 'wordList']);
try {
const newPuzzle = Wordfind.newPuzzleLax(wordList, conf);
dispatch(generatePreviewSuccess(newPuzzle));
} catch (err) {
dispatch(generatePreviewError(err.message));
}
});
};
}
export function generatePreviewError(error) {
return {
type: GENERATE_PREVIEW_ERROR,
error,
};
}
export function generatePreviewSuccess(payload) {
return {
type: GENERATE_PREVIEW_SUCCESS,
payload,
};
}
Unfortunately, the loader never appears. I console.logged the state setting the loading to true when my component renders, and it changes! I can see the log but not the loader, the component doesn't really re-render until the actions generatePreviewSuccess() or generatePreviewError() are dispatched. And it's not an issue from the loader, if I replace the newPuzzleLax function by a loop in order to make enough time to see it, I can see it!
My theory is this function Wordfind.newPuzzleLax(wordList, conf) that I use to generate the puzzle is blocking the queue of actions because on the Chrome Redux Tools I an see the first action appearing at the same time that the second one:
Link to the function.
If I add a 1-microsecond delay between the dispatch of the two actions, the loader appears... but I would really like to understand what is happening. Thank you in advance. If it's any help, I use the react-boilerplate
I also tried to transform the function generating the puzzle as an async one by doing this:
const wordFindAsync = async (wordList, conf) =>
Wordfind.newPuzzleLax(wordList, conf);
export function generatePreview() {
return (dispatch, getState) => {
dispatch(generatingPreview())
.then(() => {
const state = getState();
const conf = state.getIn(['login', 'conf']).toJS();
const wordList = state.getIn(['login', 'wordList']);
wordFindAsync(wordList, conf);
})
.then(res => dispatch(generatePreviewSuccess(res)))
.catch(err => {
dispatch(generatePreviewError(err.message));
});
};
}
In your second version you're not returning the Promise from wordFindAsync(wordList, conf) back into your original Promise chain, and so its not being resolved/waited on by then next then.
export function generatePreview() {
return (dispatch, getState) => {
dispatch(generatingPreview())
.then(() => {
const state = getState();
const conf = state.getIn(['login', 'conf']).toJS();
const wordList = state.getIn(['login', 'wordList']);
return wordFindAsync(wordList, conf); // 🌟 return your promise here
})
.then(res => dispatch(generatePreviewSuccess(res)))
.catch(err => {
dispatch(generatePreviewError(err.message));
});
};
}
Here's a simple example demoing the behavior I'm refering to.
This one will only wait 1 second until logging "done":
const waitOneSec = () =>
new Promise(resolve => {
console.log("waiting 1 secoond");
setTimeout(resolve, 1000);
});
waitOneSec()
.then(() => {
waitOneSec(); // Promise not returned
})
.then(() => console.log("done"));
Whereas this one will wait full 2 seconds until logging "done":
const waitOneSec = () =>
new Promise(resolve => {
console.log("waiting 1 secoond");
setTimeout(resolve, 1000);
});
waitOneSec()
.then(() => {
return waitOneSec(); // 🌟 Promise returned
})
.then(() => console.log("done"));
Hope that helps.

Chaining redux thunks

I want to update user details hit submit, wait for the updating and then post this to firebase.
I have the following:
export const writeToFirebase = details => {
return firebase
.database()
.ref("/")
.set({
id: details.id,
name: details.name,
username: details.username,
email: details.email
});
};
export const updateUserDetails = updatedUser => {
return dispatch => {
dispatch({ type: UPDATE_USER_DETAILS, updatedUser });
};
};
export const updateUserDetailsThenUpdateFirebase = updatedUser => {
return (dispatch, getState) => {
return dispatch(updateUserDetails(updatedUser)).then(() => {
const storeData = getState();
});
};
};
the last function is the one I will be calling on click. The middle function is gonna take the input and then update the store (this currently works correctly)
however, I'm getting it cant read then of undefined? I tried following this: https://github.com/reduxjs/redux/issues/1676
I must be really close, I just need to return a promise correctly.
any ideas?
Looks like you are missing writeToFirebase call, and calling then from the wrong place, try this code instead:
export const updateUserDetailsThenUpdateFirebase = updatedUser => {
return (dispatch, getState) => {
dispatch(updateUserDetails(updatedUser));
writeToFirebase(updatedUser).then(() => {
const storeData = getState();
// ... called after firebase finishes
});
};
};
ah, so I was very close:
amazingly, and amagically,
this works:
export const updateUserDetailsThenUpdateFirebase = updatedUser => {
return (dispatch, getState) => {
return dispatch(updateUserDetails(updatedUser)).then(() => {
const foundUser = getState().selectedUser;
const userToUpdate = getState().users.users.filter(
user => user.id === foundUser.id
);
return writeToFirebase(userToUpdate, 1);
});
};
};

React/Redux - How to invoke a second function in componentDidMount after the first one finishes fetching AJAX

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 });
});
});
}
}

What should I do if an action depend on another async action in Redux?

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);
}
}

Categories

Resources