Say there are 2 date picker objects that corresponds to start date and end date.
These exist as states, so in order to set them, you would have to call setStartDate or setEndDate which then reducer picks up and simply sets the states.
Say I also have an async action that Is called getHugeListFromServer with (REQUEST, SUCCESS, FAILURE) and is called with parameters getHugeListFromServer(startDate, endDate).
Everytime a user picks a date, I want to call the getHugeList function and update the list. If I do something like this, it does not work because the backend action is called before the date variable is set by reducer.
example_container.jsx
class example extends React.Component {
... Methods
onChangeStartDate (startDate) {
this.props.setStartDate(startDate);
this.props.getHugeListFromServer(startDate, this.props.example.endDate);
}
// similar method for endDate.
}
I have redux thunk installed and it still doesn't help my problem. I thought about having the reducer for setDate actions to return a promise and resolve it in the container before calling the backend action, but I bet there's a better way to go about this.
Can someone lay a hand? Thanks!
I thought about having the reducer for setDate actions to return a promise and resolve it in the container before calling the backend action, but I bet there's a better way to go about this.
I don't think that sounds unreasonable at all. In fact, with redux, that would probably be completely fine and possibly preferred.
However, if you want, you could look into some lifecycle methods of components such as componentWillUpdate() - this will call each time your state is about to be updated. So you could keep your handler as is but just remove this.props.getHugeListFromServer(...) from it and put it inside of thecomponentWillUpdate() hook - check the documentation link below to see examples of how to implement it.
https://developmentarc.gitbooks.io/react-indepth/content/life_cycle/update/tapping_into_componentwillupdate.html
Related
I am working on my first React Redux project.
The project depends on a server to get its data and therefore there is also a server API that needs to be called in order to get those data.
My question is about initialising state.
There are 2 ways I can think of initialising state:
1.) First pass an empty object {} as the initialised state of the Redux store then inside componentDidMount that is where I call the API to access the data in the server and then update the state of the store/app then.
2.) In the reducer of the Redux app call all relevant server method (e.g. getCategories(), getPosts(), etc.) then compose a object with all of the data e.g. {categories: categories, posts: posts} then pass this object to the reducer as the initial state.
Which way is the recommended way for Redux when dealing with data stored in the server?
If there is a 3rd or 4th way that is the Redux way or the recommended way then please share your knowledge. Thank you.
The way I work with data coming from the back end, and as far as I know is also the suggested way, is this:
In the componentDidMount method call a thunk action creator. you can read more about them in this link.
Inside that thunk action creator you first dispatch a REQUEST action, then perform a fetch to access the back end and when the response arrives you handle it on either the success or error handlers. Depending on the handler executed you dispatch either a SUCCESS or an ERROR action (and you attach to it all the relevant information that is coming from the back end so the reducer can access it).
In your reducer you write code to handle all the above actions (REQUEST, SUCCESS and ERROR). Each handler will transition your state, for instance the REQUEST can set an isFetching flag to true that will let you show a spinner in the UI, and the SUCCESS can set that flag to false and populate the state with the data coming from the back end and passed to the reducer using the action dispatched.
Once your reducer is updated, you will access that updated state from the UI, for instance using the connect react-redux function.
Regarding the initial state, it should represent a default state. for instance, it will have the isFetching flag in false and, if you are fetching a list of foods from the back end, then that list could be an empty list in your initial state. This is just a for instance of course. You need to set an initial state that makes sense to your app.
I hope this helps you.
The first way is the better way of initializing state. Your component makes the necessary API calls in componentDidMount and passes the data to Redux as payload of actions which the reducers use to update the state of your application.
The second way is not advisable. According to the Redux docs:
The reducer is a pure function that takes the previous state and an action, and returns the next state.
(previousState, action) => newState
...
It's very important that the reducer stays pure. Things you should never do inside a reducer:
Mutate its arguments;
Perform side effects like API calls and routing transitions;
Call non-pure functions, e.g. Date.now() or Math.random().
Edit:
You can also use thunk middleware and async actions to do API calls as explained in the Redux docs and #DiegoTArg's answer.
Is there any way I can access getState() from a non-React Component nor an Action file?
I have a Util file that needs to be able to call getState() (or just any way to get the most updated state). Usually I just pass in part of the state that I need for the function, but I have one particular function that loops and needs to get the latest function every time it does loop.
I was reading more into connect() but it seems like it only works for Components. I was looking into Middleware, but it seems like that only interacts with executing Actions and I'm just at a road block where I don't know how to move forward.
Thank you
You might consider leveraging the subscribe(listener) method in the Redux Store.
The subscribe method allows you to attach a callback that is called every time the state tree may have potentially changed. This would let you call getState() inside the listener to get the current state tree.
The subscribe method is a low level API and is a bit clunky. The documentation for Redux also provides some linking to examples leveraging the Observable property of the Redux store so you can do something a bit more abstracted.
In my react / redux application I often want to dispatch multiple actions after another.
Given the following example: After a successful login I want to store the user data and after that I want to initiate another async action that loads application configuration from the server.
I know that I can use redux-thunkto build an action creator like this
function loginRequestSuccess(data) {
return function(dispatch) {
dispatch(saveUserData(data.user))
dispatch(loadConfig())
}
}
So my questions are:
When the first dispatchreturns, is the state already changed by all reducers listening for that action? I'm wondering if the two dispatch calls are run strictly sequential.
Is this approach considered best practice for dispatching multiple actions? If not, what would you suggest alternatively?
Thanks for any help!
Yes redux-thunk allows you to do as you say, and yes it is a best practice for dispatching multiple actions (one that I prefer over the alternatives because of it's simplicity). The state is indeed updated as soon as you dispatch (by the time it returns), which is why you are given a function to retrieve the state in a thunk, instead of simply a reference to the state. In that way you can alter the state with one dispatch, and then call getState() to get a reference to the new state to read from if you need to.
redux-thunk is exactly what you are looking for. I consider it very good practice.
To answer your question, indeed when the first dispatch returns the state is already changed by the called reducers.
I know its a late response :-P You can do this by using Redux-Thunk and also checkout Redux-Saga. Redux Saga provides an amazing way to handle loads of actions at the same time. Below is an example of dispatching multiple actions using Redux-Thunk
function getAndLoadSider(moduleName){
return function(dispatch){
return doSomeApiCall(someData).then(function(item){
let _item = someOtherFunction(item.collections);
let _menuData = {
name: item.name,
key: item.key
};
return new Promise(function(res, rej){
dispatch(actionOne(_menuData));
res(_item);
}).then((_item) => dispatch(actionTwo(_item)))
}
}
Above method works well for your case when one action is dependent on the first. This is a promise based approach. If you don't like to do a lots of Promise coding I recommend you go for Sagas. Check out this link https://redux-saga.js.org/docs/advanced/ComposingSagas.html where you will learn how to compose sagas. Learning curve is steep; but once you are done, Sagas will make you a ninja redux dev.
Hope this helps :-)
I have a component, let's call it List, that calls an action creator Action1.getItems().
After Action1.getItems() has retrieved information from a server, I need to call another action creator: Action2.getMoreItems(), which is a totally different action in a different file.
Facts:
Both actions creators dispatch an action and change a store.
I can't call Action2.getMoreItems() inside the _onChange() of List because it throws an error "Cannot dispatch in the middle of a dispatch ..."
Possibilities?
Call Action2.getMoreItems() inside Action1.getItems() after the server returns
Add a callback so I will have Action1.getItems(callback) and call Action2.getMoreItems() inside that callback from List component.
I believe this might be a very basic use of Flux but I still can't get my head around on how to get it right, both solutions look ugly to me.
Suggestions?
Thank you!
Is getMoreItems dependent on the return value of getItems? If so, you can call the action in the .then callback of getItems.
If it's not, then you can invoke both calls at the same time and then concat the results of the promises.
So my vote is number 2 in your question. I don't think it's an ugly solution at all. You are delegating the responsibility of getting items and more items to your actions. The component itself is ignorant of that and simply dispatched a request for those items. How they're built should be transparent to the component.
Most of the flux implementations have a method like "waitFor", which lets a store wait for another store to finish.
Assuming you need two execute 2 methods in your store: GetUser and GetPermissions. The first fetches a user and the latter the permissions, the fetched user has. Obviously, GetPermissions needs to wait for the user object to be fetched from the first method.
In this case, you would dispatch an action called GetUser and make both of your store functions listen to that action. So if you dispatch GetUser both store methods are executed.
In alt.js (flux implementation) and in many others, there are methods like waitFor(store), so GetPermissions can wait for the UserStore to fetch the user. In alt.js pseudocode this would look like this:
// UserStore
function getUser() {
fetch('api/user').then(this.setState);
}
// PermissionsStore
function getPermissions() {
this.waitFor(UserStore);
let user = UserStore.getState().user;
fetch('api/permission', user).then(this.setState);
}
This is, as I said, not working pseudocode, but it shows the concept. If you are interested, the flux implementation alt.js is great. Check it out at alt.js.org
I'm in trouble with React and Flux... We have an application that is pretty similar to the new Flux chat example. We have the famous error "cannot dispatch in the middle of dispatch". But, it's hard to us to think in a good way to resolve this problem in some cases.
Our doubt is identical to this: https://groups.google.com/forum/#!topic/reactjs/mVbO3H1rICw, but I can't understand very well the solution adopted. As far as I understand, is not a very elegant solution.
Here is the sequence of events:
Action A is dispatched;
The Store updates it's internal state and emits the change message;
A react component X receives the change message (by the callback of the listener) and updates it's state (setState);
The component X renders and as part of that a new component Y is mounted too. We choose the component (Y, Z, etc...) to be rendered using the information of the state;
The new component Y needs data to display that isn't initially loaded. So we call a API in the componentDidMount() of the component Y, that calls an action B.
Then, with the new dispatcher in the Action B, we have this dispatch error.
If you consider that our application logic have some issue, I can bring some practicals examples to show why this scenario is common for us. Any idea of how refactor this "flux" is very welcome.
Thanks for any help!
i think you need to use the waitFor token from the dispatcher before launching action b (youre using the dispatcher from the flux npm module right?). additionally, what can you do is make the ajax call from your store during action A if youre always going to need that data from action b.
solution b would look like this in /* store.js */
Dispatcher.register(function(payload){
switch payload.type {
case ACTION_A:
/* do your state logic */
StoreDataAccessLayer.apiCall()
}
}
where you have a Data Access Layer class / object that wraps your ajax call to the api. using the state from your store as inputs, and calling trigger change from the success function. this is the primitive solution i've seen when using the flux npm module if youre not going with waitFor