Can't do async in redux action - reactjs

i already use redux thunk to do async. it already work with this format :
export function registerUser({email,password}){
return function(dispatch){
axios.post(`${ROOT_URL}/api/auth/signup`,{email,password})
.then(response =>{
dispatch({type:AUTH_USER});
localStorage.setItem('laravel_user_token',response.data.token);
browserHistory.push('/register');
})
.catch(response => dispatch(authError(response.data.error)));
}
}
now i want to try do some async in logout action like this :
export function logoutUser() {
console.log("logout");
localStorage.removeItem('laravel_user_token');
return { type: LOGOUT_USER }
}
that's work, Now i intend to redirect the page after logout was performed with this code:
export function logoutUser() {
return dispatch => {
console.log("logout");
localStorage.removeItem('laravel_user_token');
return dispatch({ type: LOGOUT_USER })
.then(() =>
browserHistory.push("/")
);
}
}
My problem is no responses comeback, even my console.log("logout") is not work.

There are some variants how you can perform it.
1. Via promises
This, way as you tried, is performing using Promise, so your main error - that your function is not returning Promise.
Change your action
export function logOut() {
return (dispatch) => {
return new Promise((resolve) => {
dispatch({
type : LOGOUT_USER,
});
resolve();
}
}
Your action handler can be simple reducer function, that will change the state.
case 'LOGOUT_USER' : (state) => {
return Object.assign({}, state, {
autenticated : false
})
}
And then, call it from the react component.
hireLogoutClick = () => {
this.props.logOut().then(() => browserHistory.push('/');
}
logOut function return Promise, which will call your callback function on resolve() step. Firstly will be called your reducer function and then will be called your callback (function passed in .then)
2. Via middleware
There is another variant, how to perform this, via middleware, and call it from reducer, like an action, middleware will catch it and redirect, for my opinion better, then use promises.
Create a middleware
import { browserHistory } from 'react-router'
import { ROUTING } from '../constants/ActionTypes'
export const redirect = store => next => action => {
if(action){
if (action.type === ROUTING) {
browserHistory['push'](action.nextUrl)
}
}
return next(action)
}
Bind it
// Apply the middleware to the store
const middleware = routerMiddleware(browserHistory)
const store = createStore(
reducers,
applyMiddleware(middleware)
)
Use :)
export function logOut () {
return (dispatch) => {
dispatch({
type : types.LOGOUT_USER
})
dispatch({
type : types.ROUTING,
nextUrl: '/'
})
}
}

Related

Redux Thunk Submit to Action

I been stuck to this quite a bit, I am trying to pass in my state to the redux but it seems like I am doing it wrong.
This are my code:
This is my submit function
popForm() {
let states = this.state.orders;
let d = states.filter((data) => {
return data !== null && data !== undefined
});
// console.log("d",d);
this.props.LogInClick(d);
// LogInClick(state);
}
This is my mapToDispatch
const mapDispatchToProps = (dispatch) => {
return {
LogInClick : (data) => dispatch(Actions.addDynamic(data)),
}
}
Action call
export const addDynamic = ({data}) => {
console.log("Manage to get to here");
console.log("dataInAction",data);
}
My reducer
case Actions.ADD_DYNAMIC: {
return {
...state,
data: action.payload
};
}
Your synchronous action should to return an object with type and payload.
When dealing with async actions, you need thunk(or saga etc) middleware. Your code seem to dispatch normal action (not async). So just make sure that your action returns type and payload.
Like this
export const addDynamic = ({data}) => {
console.log("Manage to get to here");
console.log("dataInAction",data);
return {
type: Action.ADD_DYNAMIC,
payload: data
}
}

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

Promise is not getting returned in the redux thunk

I am new to react-redux. Here what I am doing is that,
I have an action that is like
export const updateActivePage = (activePage) => {
return (dispatch) => {
return dispatch ({
type: UPDATE_ACTIVEPAGE,
payload: activePage
});
}
}
Now, in my container, I want to call an action which will get called after this so,
handlePageChange = (pageNumber) => {
this.props.updateActivePage(pageNumber).then(() => {
this.props.fetchUserJd(this.props.activePage);
})
}
case UPDATE_ACTIVEPAGE: {
console.log("action payload", action.payload);
return {
...state,
activePage: action.payload
}
}
So, Here I am trying to use then but I am getting an error that Uncaught TypeError: _this.props.updateActivePage(...).then is not a function
So what is it that I am doing wrong?
You are fundamentally misusing redux-thunk. The intended use for this library is to dispatch an action creator (in this case, updateActivePage), which returns a function that gets invoked with state params. You should be dispatching updateActivePage since this is your thunk. Also, this is the function which should contain your computation, which should then posit the results into state. It appears that you are calling this function directly, and for some reason expect it to return a promise, and then somehow doing some computation. You should re-visit the thunk docs.
export const updateActivePage = (activePage) => {
return (dispatch) => {
return dispatch ({
type: UPDATE_ACTIVEPAGE,
payload: activePage
});
}
}
dispatch is not a promise, It just a function.
export const updateActivePage = (activePage) => {
return (dispatch) => {
return new Promise((res, rej) => {
dispatch ({
type: UPDATE_ACTIVEPAGE,
payload: activePage
});
res(true);
})
}
}
Can you try returning promise like I mentioned above?

Redux - how to call an action and wait until it is resolved

I'm using react native + redux + redux-thunk
I do not have much experience with redux and react native
I'm calling an action inside my component.
this.props.checkClient(cliente);
if(this.props.clienteIsValid){
...
}
and within that action there is a call to an api that takes a few seconds.
export const checkClient = (cliente) => {
return dispatch => {
axios.get(`${API_HOST}/api/checkclient`, header).then(response => {
dispatch({type: CHECK_CLIENT, payload: response.data }); //valid or invalid
}).catch((error) => { });
}
}
My question is how can I delay the return of the action until the api response is completed? I need the api response to know if the client is valid or invalid. That is, I need the action to be resolved and then verify that the client is valid or invalid.
You can return a promise from the action, so that the call becomes thenable:
// Action
export const checkClient = (cliente) => {
return dispatch => {
// Return the promise
return axios.get(...).then(res => {
...
// Return something
return true;
}).catch((error) => { });
}
}
class MyComponent extends React.Component {
// Example
componentDidMount() {
this.props.checkClient(cliente)
.then(result => {
// The checkClient call is now done!
console.log(`success: ${result}`);
// Do something
})
}
}
// Connect and bind the action creators
export default connect(null, { checkClient })(MyComponent);
This might be out of scope of the question, but if you like you can use async await instead of then to handle your promise:
async componentDidMount() {
try {
const result = await this.props.checkClient(cliente);
// The checkClient call is now done!
console.log(`success: ${result}`)
// Do something
} catch (err) {
...
}
}
This does the same thing.
I don't understand the problem, but maybe this could help
export const checkClient = (cliente) => {
return dispatch => {
dispatch({type: CHECK_CLIENT_PENDING });
axios.get(`${API_HOST}/api/checkclient`, header).then(response => {
dispatch({type: CHECK_CLIENT, payload: response.data }); //valid or invalid
}).catch((error) => { });
}
}
...
this.props.checkClient(cliente);
if(this.props.clienteIsPending){
...
}
if(this.props.clienteIsValid){
...
}
I have written a full code if there is still confusion. The promise should work for a sequence of asynchronous redux action calls
Actions
export const buyBread = (args) => {
return dispatch => {
return new Promise((resolve, reject) => {
dispatch({type: BUY_BREAD_LOADING });
// or any other dispatch event
// your long running function
dispatch({type: BUY_BREAD_SUCCESS, data: 'I bought the bread'});
// or any other dispatch event
// finish the promise event
resolve();
// or reject it
reject();
});
}
export const eatBread = (args) => {
return dispatch => {
return new Promise((resolve, reject) => {
dispatch({type: EAT_BREAD_LOADING });
// or any other dispatch event
// your long running function
dispatch({type: EAT_BREAD_SUCCESS, data: 'I ate the bread'});
// or any other dispatch event
// finish the promise event
resolve();
// or reject it
reject();
});
}
Reducer
const initialState = {}
export const actionReducer = (state = initialState, payload) => {
switch (payload.type) {
case BUY_BREAD_LOADING:
return { loading: true };
case BUY_BREAD_SUCCESS:
return { loading: false, data: payload.data };
case EAT_BREAD_LOADING:
return { loading: true };
case EAT_BREAD_SUCCESS:
return { loading: false, data: payload.data };
}
Component class
import React, {Component} from 'react';
class MyComponent extends Component {
render() {
return (
<div>
<button onClick={()=>{
this.props.buyBread().then(result =>
this.props.eatBread();
// to get some value in result pass argument in resolve() function
);
}}>I am hungry. Feed me</button>
</div>
);
}
}
const mapStateToProps = (state) => ({
actionReducer: state.actionReducer,
});
const actionCreators = {
buyBread: buyBread,
eatBread: eatBread
};
export default connect(mapStateToProps, actionCreators)(MyComponent));

Displaying notification after successful async redux action

I'm trying to figure out the best way to display a sweetalert message after a successful async action. So I have an ExcursionDetail component that allows you to book the excursion. Here is the simplified component:
class ExcursionDetails extends Component {
bookExcursion() {
const userId = jwt_decode(localStorage.getItem('token')).sub;
this
.props
.actions
.bookExcursion(userId, this.props.match.params.id);
}
render() {
....
<RaisedButton label="Book Excursion" onClick={e => this.bookExcursion()}/>
....
)
}
}
const mapStateToProps = (state, ownProps) => {
return {excursion: state.excursion}
}
const mapDispatchToProps = (dispatch) => {
return {
actions: bindActionCreators(ExcursionActions, dispatch)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(ExcursionDetails);
The action creator:
export const bookExcursion = (userId, excursionId) => {
return (dispatch, state) => {
dispatch(requestBookExcursions())
return ExcursionApi
.bookExcursion(userId, excursionId)
.then(resp => {
if (resp.ok) {
return resp
.json()
.then(payload => {
dispatch(bookExcursionsSuccess(payload.data));
})
}
}).catch(err => {
dispatch(bookExcursionsFailed(err));
})
}
}
What would be the best practice to then display the sweet alert notification? The options I thought of were:
Add a bookSuccess property that I can view if true or false in my ExcursionDetails component and if true call the sweetalert function.
Create notification specific actions and reducers and listen for it in my components. Only issue with this is I would need to have some sort of setTimeout after every notification call to clear the notification reducer and this seems a bit hacky.
call the sweet alert function within my reducer
pass a callback to the action creator
redux-thunk returns a promise; however even if the http call fails it will return a successful promise so this option doesn't seem viable.
I would and is using the first option that you mentioned.
I have created a new component and pass the redux store using connect. I check for it if the value is true on componentWillReceiveProps and set the state according and then you can display your sweetalert.
Well you can call it in the action creator.
You can use something like toastr.
Simple and clean.
export const bookExcursion = (userId, excursionId) => {
return (dispatch, state) => {
dispatch(requestBookExcursions())
return ExcursionApi
.bookExcursion(userId, excursionId)
.then(resp => {
if (resp.ok) {
return resp
.json()
.then(payload => {
dispatch(bookExcursionsSuccess(payload.data));
//Here is your notification.
toastr.success('Have fun storming the castle!', 'Miracle Max Says')
})
}
}).catch(err => {
dispatch(bookExcursionsFailed(err));
})
}
}

Resources