fix(ui): stop the book page refetching forever for a book that does not exist - #272
Open
jordanfelle wants to merge 2 commits into
Open
jordanfelle wants to merge 2 commits into
jordanfelle wants to merge 2 commits into
Conversation
…ot exist Opening /book/<id> for a book that no longer exists showed a spinner indefinitely instead of the existing "Sorry, that book cannot be found" screen. BookDetailsPageConnector fetches the book when it is not in the store (needsBookFetch = not fetching && no book). For a missing book the server answers 404 (GET /book?bookId=<id>), the FETCH_BOOKS handler's failure branch sets isFetching=false, and needsBookFetch flips false -> true again; componentDidUpdate treats that transition as a reason to populate() and fetches again. That is an endless loop of failing requests with the spinner up most of the time, so the NotFound render is never reached. Remember the route key that was fetched and only fetch once per route (same for the author fetch, which has the same shape if an author is missing). Once that one fetch fails, isFetching is false, there is no book, and the existing NotFound screen renders. Any bookmark or list entry pointing at a pruned or deleted book hit this.
Adversarial review of Chaptarr#272: a once-per-route guard would leave 'cannot be found' up after a transient 500, a network error, or a request aborted by another fetchBooks, and also if the book is dropped from the store while the page is open. A definitive 404 is now final (that is the loop being fixed); any other outcome is retried up to 3 fetches per route (MAX_BOOK_FETCH_ATTEMPTS), so a persistent failure still ends on the NotFound screen. Adds booksError to the connector props and resets the counter when the route key changes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Opening
/book/<id>for a book that no longer exists (deleted, or pruned by a metadata profile) shows a spinner indefinitely instead of the existing "Sorry, that book cannot be found" screen.Cause
BookDetailsPageConnectorfetches the book when it is not in the store (needsBookFetch = !isFetching && !hasBook). For a missing book the server answers 404 (GET /book?bookId=<id>; checked against a live instance: 404 in ~1 ms). TheFETCH_BOOKSfailure branch setsisFetching: false, soneedsBookFetchflipsfalse -> true, andcomponentDidUpdatetreats that transition as a reason topopulate()again. The result is an endless loop of failing requests with the spinner up most of the time, so theNotFoundrender at the bottom ofrender()is never reached.Change
NotFoundscreen renders.fetchBooks, an empty 200) is retried, up to 3 fetches per route (MAX_BOOK_FETCH_ATTEMPTS), so a persistent failure still ends onNotFoundinstead of looping, and a transient one still recovers.books.errorto tell a 404 from other failures.Testing
componentDidUpdatecycle in plain JavaScript (this checks the reasoning, not the React component): book deleted (404 every time) = 1 fetch then NotFound; transient 500 then success = 2 fetches then the book; aborted request then success = 2 fetches then the book; persistent 500 = 3 fetches then NotFound; empty 200 forever = 3 fetches then NotFound; book exists = 1 fetch. Without the guard the 404 case fetches on every cycle (stopped by my cap at 40).