How to show a message after Redux dispatch - reactjs

When a button is clicked I call the following:
this.props.dispatch(addNote(this.state.noteContent))
This in turn calls the following action:
export default function addNote(note){
return dispatch => {
axios.post('http://localhost:2403/notes', {
Body: note
})
.then(function (response) {
dispatch({
type: ADD_NOTE,
payload: response.data
})
})
.catch(function (error) {
console.log(error)
})
}
}
My reducer then updates the state and a new note is added to the list, so it's all working correctly.
However I now want to show a message in the UI which says "Note has been added successfully". Ideally, this would be a part of a wider notifications system, but for now, I just need to figure out how to report success (or failure) on this one action.
How do I achieve this?

You can return a promise from your dispatch using redux-thunk, why not use that? Try this code in your thunk:
export default function addNote(note){
return dispatch => {
return axios.post('http://localhost:2403/notes', {
Body: note
})
... //function continues
Then you can do:
this.props.dispatch(addNote(this.state.noteContent)).then(successFunc, failureFunc);
successFunc = function() {
//Show message on UI
}

You can use a toast component (like this https://www.npmjs.com/package/react-toastify) that sits on top of your routes in your app that renders every time an action in redux is called.
In each of your action functions, you can have a call to a message reducer that updates the state inside message reducer. Each time the state in that reducer is changed, the toast component will re-render and last for a certain amount of time or until a user manually closes the toast component.

Related

dispatching actions within nested functions in redux-saga

When a user submits a form I want them to be prompted with multiple modals.
Basic structure:
My app is set up so that to show a modal all you have to do is dispatch an action with the modal body as the payload fo the action.
dispatch({type: SHOW_MODAL, payload: <MyModal />})
When the user submits the form an action is dispatched that is picked up by a saga, so now we are in saga land. What I'd like to do is have the user be shown several modals sequentially before the form is actually submitted to the backend.
// mySaga.js
function* submitForm() {
// show a modal
// then show another modal
// then submit the form
}
What is the best way of doing this? What makes the most sense to me is to use promises.
// mySaga.js
function* submitForm() {
yield call(() => {
new Promise( resolve => {
yield put({type: SHOW_MODAL, payload: <MyModal onClick={resolve} />})
})
})
...
// add as many more modals as I'd like
...
yield call(myApiCall)
}
The problem with the above is that you can't use a yield inside of that promise function because it isn't a generator. All I need is a way to do a normal dispatch of an action inside of a saga, but in looking all over the internet that does not seem trivial at all.
Am I missing something about sagas? What is the best way to do this?
I suggest to change your program a little.
It is not a good idea to dispatch <Modal/> component to store. Although you can store component inside store, but it will be difficult to pass correct props to component.
I suggest to have a variable in store, like firstModalOpened which will control if modal is showing. You can set this variable in saga and await for an action to change this variable.
// mySaga.js
function* submitForm() {
yield put({type: SHOW_MODAL, firstModalOpened: true});
take('FIRST_MODAL_CLOSED'); // Await for modal close action
...
// add as many more modals as I'd like
...
yield call(myApiCall)
}
In React the <Modal/> compoennt can be used as follows
<Modal open={props.firstModalOpened} onClose={() => dispatch({type: 'FIRST_MODAL_CLOSED'})}/>
If you have several modals which will be opened simultaneously you can call put several times and then await for all close actions to arrive before proceeding to yield call(myApiCall)
yield put({type: SHOW_MODAL, firstModalOpened: true});
yield put({type: SHOW_MODAL, secondModalOpened: true});
yield all([
take('FIRST_MODAL_CLOSED')
take('SECOND_MODAL_CLOSED')
]);

How to do something inside component after triggering a redux action?

i want to domething inside component after an action is done.
for example i want to show a modal to user after a request is successfully done, or disable some elements if request is done successfully.
should i use callbacks? or promise? if yes, then how
export const fetchHorizontalSpecialProductsList=(virtinId)=> {
return (dispatch) => {
dispatch({
type: Types.REQUEST_FETCH,
});
HomeApi().specialProducts({vitrinId:virtinId,rows:8,page:0,frontTypeList:["SPECIAL"]}).then((response) => {
dispatch({
type: Types.REQUEST_SUCCESS,
payload: response,
});
//
.then(probably here)
//
}).catch((response) => {
dispatch({
type: Types.REQUEST_FETCH_FAIL,
payload: response,
});
});
};
};
when the request is successful i need to show a modal
You just need to connect to Redux store, for example, in Redux state you should declare these properties:
success: false/true.
Then with the action received is REQUEST_FETCH_FAIL or REQUEST_SUCCESS you just need to use the switch..case statement in reducer to change the variable to true/false.
Your container connected to Redux store, depending on the props success true/false, you can doSomething() you would like to do,

React Redux best practices for performing actions

Where should I perform actions (redirecting or adding/removing something to/in the localstorage) in React (and Redux)? So after the password is successfully updated I want to redirect the user to another page. Should I redirect after the dispatch method, should I do it in the component or are there other options?
Example action:
export function updateAccountPassword(encryptedPassword) {
return dispatch => {
axios.post(API_URL + '/account/recovery/update', {
_id: getSignedInUserID(),
password: encryptedPassword
}).then(() => {
dispatch(updateUserPasswordSuccess())
}).catch(() => {
dispatch(updateUserPasswordFailError());
})
}
}
function updateUserPasswordSuccess() {
return({
type: RECOVERY_UPDATE_SUCCESS
})
}
function updateUserPasswordFailError() {
return({
type: RECOVERY_UPDATE_FAIL_ERROR,
payload: 'Something went wrong, please try again'
})
}
The way I am doing it is by passing the this.props.history.push as a callback to the action creator, and calling it, as you suggested, in the action creator, after dispatch.
Here is an example from my code:
In the component form's submission, calling the action creator:
this.props.ACTION_CREATOR(formPayload, () => {
this.props.history.push(`ROUTING_TARGET`);
});
And, then, in the action creator, when the proper condition has been met, calling the callback (rerouting).

redirect from component level after specific dispatch - redux thunk

I have a fairly simple use case, but having a hard to find the appropriate answer. I'm using React,Redux,React Router & redux thunk middleware.
Lets say, I have two module food-tags & food. These modules have individual create,list,edit page/component. In practical use case, food-tags have no special value. Whenever a food object is created, separated tags are inserted into the food object's tags property.
General use case is that, after any item is created successfully, react router redirects it to the list page.
whenever i'm calling the createTag action from food-tag module, I can do it in a hacky way. like just after the success dispatch, i can call
browserHistory.push('/dashboard/tags')
this leads me to a problem where i can create food-tag inline from the food create component. Codes are given below
actions.js
export function createTag(tag) {
return function (dispatch) {
axios.post(API_URL + 'api/tags', tag)
.then((response) => {
// I CAN DO REDIRECT HERE,BUT THIS CAUSES THE PROBLEM
dispatch({type: 'TAG_CREATE_RESOLVED', payload:response});
toastr.success('Tag created Successfully.......!');
})
.catch((err) => {
dispatch({type: 'TAG_CREATE_REJECTED', payload: err});
toastr.warning(err.message);
})
}
}
component/container.js
createTag () {
//validatation & others....
this.props.createTag(tag)
}
react-redux connection
function mapDispatchToProps (dispatch) {
return bindActionCreators({
createTag: createTag
}, dispatch)
}
Almost same pattern in food/create.js
$('#food-tags').select2(select2settings).on('select2:selecting', function (event) {
let isNewTagCreated = event.params.args.data.newOption,
name = event.params.args.data.text;
if (isNewTagCreated && name !== '') {
reactDOM.props.createTag({name}); // reactDOM = this context here
}
});
What I want basically that, I want to get access in the component level which action type is dispatching so that i can redirect from component & show notifications as well instead of action thunk. May be i'm not thinking in the proper way. there could be a dead simple work around.
It's good to know that redux-thunk passed out return value from the function. So you can return the promise from the action creator and wait until it will be finished in you component code
export function createTag(tag) {
return function (dispatch) {
return axios.post(API_URL + 'api/tags', tag) // return value is important here
.then((response) => dispatch({type: 'TAG_CREATE_RESOLVED', payload:response}))
.catch((err) => {
dispatch({type: 'TAG_CREATE_REJECTED', payload: err})
throw err; // you need to throw again to make it possible add more error handlers in component
})
}
}
Then in your component code
createTag () {
this.props.createTag(tag)
.then(() => {
toastr.success('Tag created Successfully.......!');
this.props.router.push() // I assume that you have wrapped into `withRouter`
})
.catch(err => {
toastr.warning(err.message);
});
}
Now you have proper split up between action logic and user interface.

React-redux cross access state value

For last two weeks I have been working with redux and I'm facing an issue where I want to access/change a state value of another reducer. How can I achieve that?
For example: I have two components 'A-Component' and 'Message-component'
which has 'A-actions', 'Message-actions' and 'A-reducer', 'Message-reducer' respectively
When an action of 'A-Component' is called it will call the corresponding reducer function where I need to update the Message-reducer state value which will display the message box
A-action
export function add(data) {
return {
types: [types.ONADD, types.ONADDSUCCESS, types.ONADDFAIL],
payload: {
response: api.add(data).then(response => response),
data
}
};
}
A-reducer
export default createReducer(initialState, {
[types.ONADD](state) {
return {
...state,
message: 'Updating Records'
};
}
});
The above mentioned message state value is message reducer's state value. I want to update the message state value from A-reducer
which in turn updates the message component. Is this possible in redux?
I tried with various middleware but failed.
Thank in advance!
I think you're approaching this the wrong way. You should normalize your data as much as you can, and then maybe use the connect decorator to compose the state you need for your UI. For example, Messages could be nested under a "Friend"'s node, but it's better to have them in their own store, and then make a function that selects the messages from a friend based on a relationship. This gives you aggregations (You have 3 unread messages) for free. Take a look at reselect for a way to do this in a nice (and cached) way.
Edit:
You could write middleware which dispatches multiple actions:
export default (store) => (next) => (action) => {
if(!action.types){
return next(action);
}
action.types.forEach(type => {
next({
type,
payload: action.payload
})
});
}
Then call it from an Action Creator like so:
export function addMessage(message){
return {
types: ['ADD_MESSAGE', 'UPDATE_USER'],
payload: message
}
}
If you already have a update action in Message-actions
I think you can just directly dispatch the update action when ONADDSUCCESS is triggered.
// Message action
export function MessageUpdate (data) {
return {
type: ...,
data,
}
}
// A action
export function add(data) {
return dispatch => {
dispatch({
type: types.ONADD
});
// code for your add event
api.add(data).then( response => {
(() => {
dispatch(MessageUpdate(response));
return dispatch({
type: types.ONADDSUCCESS,
})
})()
});
}
}
Hope this answer to your question.

Resources