From research, I'm seeing that thunk is the tool you use to be able to chain actions together and/or deal with callbacks, asyc actions, and side-effects.
I'm having trouble understanding the thunk middleware guide. They reference 'store.dispatch' all the time (as do most tutorials on redux) yet I never actually call dispatch and never have access to 'store' so I'm unsure how to implement anything they propose. (I assume this is because I use mapDispatchToProps in react.... which isn't an option in my action creators files)
Below is my action creator (some code removed to clarity):
import { CREATE_NEW_SAMPLING_EVENT } from '../Constants/ActionTypes';
import { emptySamplingEvent } from '../Constants/SamplingEvent';
import _ from 'lodash';
import uuidv4 from 'uuid';
export function createNewSamplingEvent(eventName) {
let newEvent = _.cloneDeep(emptySamplingEvent);
newEvent.eventID = uuidv4();
newEvent.eventName = eventName;
newEvent.dateModified = new Date();
//TODO: call an optional callback
//TODO: dispatch eventLinkTable event
return { type: CREATE_NEW_SAMPLING_EVENT, event: newEvent }
}
What I would like to do is listed in the 'todo's.
I have another action, called EVENT_LINK_TABLE in a different action creator file and different reducer, that would take the uuid from this action creator as an argument. I'd like to dispatch this EVENT_LINK_TABLE action (with the uuid) as soon as I'm done making this a new event.
Further, I'd like to call a standard callback (which will actually be dispatching another action - LOAD_SAMPLNG_EVENT).. but I'm unsure how to call the callback AND return the action. I'm also hearing that doing that from the action creator is bad practice, as well as I don't want it to happen if there is a failure in the creation process.
To add additional info, this is where I'm dispatching the action in my react code:\
handleBrandNewButtonClick = () => {
this.props.createNewSamplingEvent(this.state.newSamplingEventName);
}
This component is 'connect'ed thusly:
const mapStateToProps = function (state) {
return {
samplingEvents: state.SamplingEvents, //to get list of sampling events to check for uniqueness for that user
}
}
const mapDispatchToProps = {
createNewSamplingEvent,
}
export default withRouter(
withStyles(styles, { withTheme: true })
(connect(mapStateToProps, mapDispatchToProps)
(NewEventForm)
)
);
Looks like you don't have proper knowledge in redux. I'll tell you how redux store works. Then you will be able to understand redux.
In redux, we have five things,
Action creator, Action, Dispatcher, Reducers, Store
Imagine you thought to open an insurance company. Ok, here we have our first client. He came to the company and ask for, 'Hey I need to open a new account'.
Now the front desk person will say, 'Ok fill this form and give it to me' Once he gave the form to the front desk, then the person can leave.
In redux, this person is known as Action Creator
The form itself know as the object that returns from action creator
The front-desk person know as Dispatcher
Once the dispatcher got the form, he will make some photocopies of it and send to all departments in your company. policy department, Accounting department, Claims department, etc
These departments are known as reducers
Now each department will check what kind of form is this. Or it's about opening a new account. Ok, Accounting department will get the sum of money and add it to the company vault. Also, the policy department will make a new account for the client. But, the Claims department will not care about this form because it is not about a claim. This is why we set a 'type' property to the object.
Actions creators should only return plain objects. For example, if there is an async call in the action creator it does not return a plain object so we need to have some middleware to avoid this problem. Here we have redux-thunk. This middleware will help us to manually do the dispatch process.
So we get the dispatcher as the parameter to the action creator. Then, once we get the results from the async process, now we can dispatch the results manually inside that action creator. This redux thunk act as a middleware between the dispatcher and the reducers.
You can run business logic inside a function and dispatch the action itself. The action creator, setNewsCreator creates a POJO. That POJO is then dispatched as an action that will get picked up by the reducer
// action creator
const setNewsCreator = ({ news }) => ({ type: 'SET_NEWS', news })
const fetchNews = () => async dispatch => {
const response = await getNews()
dispatch(setNewsCreator({ news: response }))
}
and its usage in a component
componentDidMount() {
this.props.fetchNews() // assuming you've added this function to your mapDispatchToProps
}
Apologies for my first comment. In hindsight, I was not explaining actions correctly.
Related
This is my first stackoverflow questions please excuse if there is any mistake.
I am a beginner to react, redux and saga.
I am trying to dispatch an action which will be handled by saga and then dispatch another action from within saga. While I do this I get this below error message :
"You can't put (a.k.a. dispatch from saga) frozen actions. We have to
define a special non-enumerable property on those actions for
scheduling purposes. Otherwise you wouldn't be able to communicate
properly between sagas & other subscribers (action ordering would
become far less predictable). If you are using redux and you care
about this behaviour (frozen actions), then you might want to switch
to freezing actions in a middleware rather than in action creator.
Example implementation:
const freezeActions = store => next => action =>
next(Object.freeze(action))"
I haven't explicitly frozen my actions anywhere. I am just calling a function which returns an action object. I dont understand why saga complains that its frozen object.
I have reproduced my error in this sandbox : https://codesandbox.io/s/elastic-zhukovsky-ntmfn
So solution was very simple. When I was trying to import action creator function I was not importing within {}. Action creator was not a default export from where it was defined.
import { setSnackbar } from "../../ducks/snackbar"; //---> senSnackbar should be imported within brackets.
//import setSnackbar from "../../ducks/snackbar"; //->dont do this
import { call, put } from "redux-saga/effects";
export function* handleCheckCoin(action) {
try {
let strSymbol = action.payload;
console.log("Symbol : " + JSON.stringify(strSymbol));
///send out a api request to check Crypto Symbol is valid
yield put(setSnackbar(true, "success", "message"));
} catch (error) {
console.log("error while launching set snack bar action : " + error);
}
}
My assumption from working with redux is that dispatching actions is a synchronous task.
fire action 1 - > store updated
fire action 2 -> store updated
In a project I'm currently working on, I have a product customizer, that allows some user selection, they can place multiple orders, but if they're only ordering their current selection and select "purchase", I fire "addOrder", adding their selection to the orders array, and then the "purchase" action, which is a thunk submitting the orders stored in redux to my cart API.
I've expected that I would be able to rely on the store being in a consistent state, reliably after each action, and so when that second action fires it would have the state, as it is, after the first regular action fired before it, but no dice.
Are my expectations and understanding of redux here incorrect?
If so, Is redux thunk acting outside the normal dispatch in some way?
in my connected component I dispatch each action:
//.... inside component
purchase = () => {
this.props.addOrder(); // regular action
this.props.purchase(); // thunk
};
// ... rest of component
Yes, dispatching is always 100% synchronous, unless altered by a middleware. And yes, by default, you can call getState() again after a dispatch to get the updated state:
function checkStateAfterDispatch() {
return (dispatch, getState) => {
const firstState = getState();
dispatch({type : "FIRST_ACTION"});
const secondState = getState();
if(secondState.someField != firstState.someField) {
dispatch({type : "SECOND_ACTION"});
}
}
}
I am new to react and redux xo my questions will sound basic.
What does dispatch means? I am referring to the term dispatching an action.
Why do we need mapDispatchToProps to store actions on redux? We can simply import an action and use it. I have a scenario in which I have to load data when a component is mounted.
#mariazahid mapDispatchToProps will bind the action to your component so that you can pass it down to your presentation components. This is a pattern that is normally used within using Redux with React.
You can import your action and just dispatch the action, but in most scenarios a container -> component pattern is used. A container is where the actions are mapped to and the state and the only goal of this component is to pass this data down to components that are used for presenting that data.
When working in teams, it's a pattern that is easily adoptable. Instead of importing actions from left right and center, you will just have to be aware of the container and how it passes the required actions/data down to the children.
From an implementation perspective, dispatch is just a method that is used to communicate with your reducers
Let say that your action looks something like this
function myAction() {
return { type: 'MY_ACTION' };
}
You're trying to communicate with the reducer that responds to the action type 'MY_ACTION'
In your mapDispatchToProps you'd typically do something like this;
function mapDispatchToProps(dispatch) {
return { actions: bindActionCreators(myActions, dispatch) }
}
Effectively, you're wrapping(binding) your actions to the dispatch method;
function bindActionCreators(actions, dispatch) {
// this is a very trivial implementation of what bindActionCreators does
let wrappedActions = {};
Object.keys(actions).forEach(action =>
// for every action, return a function that calls dispatch on the result of what your action returns
return function(...args) {
// remember here that dispatch is the only way you can communicate with the reducers and you're action's type will determine which reducer responds to return the new state
return dispatch(actions[action](..args));
}
);
}
And so, these "bound" actions are now assigned to a props.actions in your component.
In React with Redux, when there are some user operations, e.g., in facebook, user adds some comments, I will call dispatch() to send the add action to redux store, but when should I call back end API to save these data to database? do I need to do it together with dispatch()?
thanks
One solution would be to transfer your API logic into a thunk using a middleware package such redux-thunk (or similar).
Using thunks allows you to treat special kinds of actions as functions which means you can extend a plain action with specific action-related logic. The example you give of needing to serialize your state is an excellent use-case for redux-thunk.
You should note that, unlike reducers, thunks explicitly support fetching state and dispatching subsequent actions via the getState and dispatch functions.
Below is an ES6 example of how such a multi-purpose thunk might look.
To demo the getState() method the new item will only be saved via the api only if the redux state shouldSave value is truthy.
I would also use the async/await syntax to make sure the the api call succeeds before dispatching the local redux action.
Thunk Example - adding a new item
import api from './api'
export const addNew = async (item) => {
return (dispatch, getState) => {
try{
const state = getState()
if(state.shouldSave){
await api.save(item)
}
dispatch({
type: ITEM_ADD_NEW,
data: item
})
}catch(err){
const error = new Error("There was a problem adding the new item")
error.inner=err
throw(error)
}
}
}
In my project I have action creator that depend on values that are in the state of the application to generate a new value or to decide what action to dispatch. My question is to know which is the right way to do it. I thought of two ways. Access those values within the action creator:
export const changePreviousPage = () => {
return (dispatch, getState) => {
let pagination = getState().appReducers.availability.pagination;
let previousPage = pagination.actualPage != 1 ? pagination.actualPage - 1 : pagination.actualPage;
dispatch({
type: types.CHANGE_PREVIOUS_PAGE,
previousPage
});
}
};
The other option I thought was to pass the value from the component to the action creator:
In my component
class Pagination extends Component {
...
handlePreviousPage() {
const {pagination} = this.props;
this.props.changePreviousPage(pagination);
}
...
}
In my action creator
export const changePreviousPage = pagination => {
let previousPage = pagination.actualPage != 1 ? pagination.actualPage - 1 : pagination.actualPage;
return{
type: types.CHANGE_PREVIOUS_PAGE,
previousPage
}
};
What is the best way to address it ?
In my opinion always use/retrieve the state at the closest time to execution, here the action creator (or rather more specifically the thunk you are returning that would then execute).
Remember that dispatch may have any number of middleware running before the actual store.dispatch call. This can include async middleware, so the state may have changed in between calling the dispatch and the store.dispatch call it will ultimately run.
Another one to consider is you may be dispatching multiple things in an action creator which change the state and invalidate what you passed into the action creator at the top. Also a reason why I consider let state = getState() at the top of an action creator a bad idea unless you are very sure nothing is going to change during your processing (as soon as you involve any API calls I would always use getState() again instead of using a stored variable).
Also putting data from state into props (using a redux container and connect helper method) will cause a rerender every time this changes, which could have a performance impact in some cases.
My personal coding preference is also to keep things as simple as possible in mapDispatchToProps (assuming that is where you're passing in your handlers like handlePreviousPage) and avoid any data processing (in your example it's not much, but you can easily see how that may get out of hand if you're preparing data for your action creator).