Tuesday, June 4, 2024
 Popular · Latest · Hot · Upcoming
76
rated 0 times [  78] [ 2]  / answers: 1 / hits: 15400  / 8 Years ago, fri, june 10, 2016, 12:00:00

We have an action that fetches an object async, let's call it getPostDetails, that takes a parameter of which post to fetch by
an id. The user is presented with a list of posts and can click on one to get some details.



If a user clicks on Post #1, we dispatch a GET_POST action which might look something like this.



const getPostDetails = (id) => ({
type: c.GET_POST_DETAILS,
promise: (http) => http.get(`http://example.com/posts/#${id}`),
returnKey: 'facebookData'
})


This is picked up by a middleware, which adds a success handler to the promise, which will call an action like
GET_POST__OK with the deserialized JSON object. The reducer sees this object and applies it to a store. A typical
__OK reducer looks like this.



[c.GET_ALL__OK]: (state, response) => assign(state, {
currentPost: response.postDetails
})


Later down the line we have a component that looks at currentPost and displays the details for the current post.



However, we have a race condition. If a user submits two GET_POST_DETAILS actions one right after the other, there is
no guarantee what order we recieve the __OK actions in, if the second http request finishes before the first, the
state will become incorrect.



    Action                   => Result
---------------------------------------------------------------------------------
|T| User Clicks Post #1 => GET_POST for #1 dispatched => Http Request #1 pending
|i| User Clicks Post #2 => GET_POST for #2 dispatched => Http Request #2 pending
|m| Http Request #2 Resolves => Results for #2 added to state
|e| Http Request #1 Resolves => Results for #1 added to state
V


How can we make sure the last item the user clicked always will take priority?


More From » redux

 Answers
11

The problem is due to suboptimal state organization.


In a Redux app, state keys like currentPost are usually an anti-pattern. If you have to “reset” the state every time you navigate to another page, you'll lose one of the main benefits of Redux (or Flux): caching. For example, you can no longer navigate back instantly if any navigation resets the state and refetches the data.


A better way to store this information would be to separate postsById and currentPostId:


{
currentPostId: 1,
postsById: {
1: { ... },
2: { ... },
3: { ... }
}
}

Now you can fetch as many posts at the same time as you like, and independently merge them into the postsById cache without worrying whether the fetched post is the current one.


Inside your component, you would always read state.postsById[state.currentPostId], or better, export getCurrentPost(state) selector from the reducer file so that the component doesn’t depend on specific state shape.


Now there are no race conditions and you have a cache of posts so you don’t need to refetch when you go back. Later if you want the current post to be controlled from the URL bar, you can remove currentPostId from Redux state completely, and instead read it from your router—the rest of the logic would stay the same.




While this isn’t strictly the same, I happen to have another example with a similar problem. Check out the code before and the code after. It’s not quite the same as your question, but hopefully it shows how state organization can help avoid race conditions and inconsistent props.


I also recorded a free video series that explains these topics so you might want to check it out.


[#61826] Wednesday, June 8, 2016, 8 Years  [reply] [flag answer]
Only authorized users can answer the question. Please sign in first, or register a free account.
carrington

Total Points: 674
Total Questions: 90
Total Answers: 108

Location: Burundi
Member since Sat, Aug 21, 2021
3 Years ago
;