-
Notifications
You must be signed in to change notification settings - Fork 4.7k
ServerSideRender: Fix data loading in development mode #62140
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -98,7 +98,7 @@ export default function ServerSideRender( props ) { | |
| LoadingResponsePlaceholder = DefaultLoadingResponsePlaceholder, | ||
| } = props; | ||
|
|
||
| const isMountedRef = useRef( true ); | ||
| const isMountedRef = useRef( false ); | ||
| const [ showLoader, setShowLoader ] = useState( false ); | ||
| const fetchRequestRef = useRef(); | ||
| const [ response, setResponse ] = useState( null ); | ||
|
|
@@ -112,6 +112,11 @@ export default function ServerSideRender( props ) { | |
|
|
||
| setIsLoading( true ); | ||
|
|
||
| // Schedule showing the Spinner after 1 second. | ||
| const timeout = setTimeout( () => { | ||
| setShowLoader( true ); | ||
| }, 1000 ); | ||
|
|
||
| let sanitizedAttributes = | ||
| attributes && | ||
| __experimentalSanitizeBlockAttributes( block, attributes ); | ||
|
|
@@ -165,6 +170,9 @@ export default function ServerSideRender( props ) { | |
| fetchRequest === fetchRequestRef.current | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess to be completely correct, we should cancel the fetch request if the component unmounts? Then we also don't need a mount ref?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I tried only to change what was necessary for the fix + minor colocation. If you ask me, this whole component needs proper refactoring 😅 |
||
| ) { | ||
| setIsLoading( false ); | ||
| // Cancel the timeout to show the Spinner. | ||
| setShowLoader( false ); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| clearTimeout( timeout ); | ||
| } | ||
| } ) ); | ||
|
|
||
|
|
@@ -175,12 +183,12 @@ export default function ServerSideRender( props ) { | |
|
|
||
| // When the component unmounts, set isMountedRef to false. This will | ||
| // let the async fetch callbacks know when to stop. | ||
| useEffect( | ||
| () => () => { | ||
| useEffect( () => { | ||
| isMountedRef.current = true; | ||
| return () => { | ||
| isMountedRef.current = false; | ||
| }, | ||
| [] | ||
| ); | ||
| }; | ||
| }, [] ); | ||
|
|
||
| useEffect( () => { | ||
| // Don't debounce the first fetch. This ensures that the first render | ||
|
|
@@ -192,21 +200,6 @@ export default function ServerSideRender( props ) { | |
| } | ||
| } ); | ||
|
|
||
| /** | ||
| * Effect to handle showing the loading placeholder. | ||
| * Show it only if there is no previous response or | ||
| * the request takes more than one second. | ||
| */ | ||
| useEffect( () => { | ||
| if ( ! isLoading ) { | ||
| return; | ||
| } | ||
| const timeout = setTimeout( () => { | ||
| setShowLoader( true ); | ||
| }, 1000 ); | ||
| return () => clearTimeout( timeout ); | ||
| }, [ isLoading ] ); | ||
|
|
||
| const hasResponse = !! response; | ||
| const hasEmptyResponse = response === ''; | ||
| const hasError = response?.error; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But it did mount already, didn't it? Unless I misunderstand something, this looks wrong - can you elaborate?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Setting the initial value doesn't mean that the component was mounted. React can call a component (function) for other reasons without mounting it.
This is a common (anti)pattern when you want to run synchronization only after the initial mount.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tyxla, I think this is the third issue I fixed for a similar pattern. I explained why the original implementations were failing in development mode here: #62141 (comment).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, of course, got it. Wow, this component is messy 😉
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it's bit of noodle soup 🍜