-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
Refactor ServerSideRender to use React hooks. #28297
Refactor ServerSideRender to use React hooks. #28297
Conversation
Size Change: -266 B (0%) Total Size: 1.39 MB
ℹ️ View Unchanged
|
b6c530e
to
30ccdb6
Compare
785c9d3
to
b0cd3c7
Compare
b0cd3c7
to
6cf8653
Compare
// When the component unmounts, set isMountedRef to false. This will | ||
// let the async fetch callbacks know when to stop. | ||
useEffect( | ||
() => () => { | ||
isMountedRef.current = false; | ||
}, | ||
[] | ||
); | ||
|
||
useEffect( () => { | ||
// Don't debounce the first fetch. This ensures that the first render | ||
// shows data as soon as possible | ||
if ( prevProps === undefined ) { | ||
fetchData(); | ||
} else if ( ! isEqual( prevProps, props ) ) { | ||
debouncedFetchData(); | ||
} | ||
} ); |
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.
Nitpicking: I think we can potentially tidy these up into just one effect. IMHO it's clearer and we can save the need for comparing isEqual
on every render.
const isMountedRef = useRef(false);
useEffect(() => {
function fetchData() {
// ...
}
// Initial request
if (!isMountedRef.current) {
fetchData();
}
isMountedRef.current = true;
const debounceFetchData = debounce(fetchData, 500);
// Debounced request
debounceFetchData();
return () => {
isMountedRef.current = false;
}
}, [...])
It's not tested so I might have missed something though.
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.
I don't think the suggested code would work:
- It would cause
fetchData
to be called twice on the first render. - It would cause
debounceFetchData
to be called on every render, which would causeresponse
to be set on every render, thus causing an infinite loop, if I had to guess. The comparison against the previous props exists to prevent this.
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.
Ahh, yeah, you're right, I missed that part. I still believe we can somehow skip the isEqual
comparison though, but that's not very important anyway.
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.
There's a pretty good article I found recently discussing this kinda stuff:
https://www.benmvp.com/blog/object-array-dependencies-react-useEffect-hook/
In this case, the useEffect
callback is dependent upon attributes
, block
, urlQueryArgs
, and httpMethod
; the first 3 of those are objects (meaning we can't just use shallow equality checks), and using destructuring on the first 2 won't work since their properties may themselves be objects.
As suggested in the article, a useDeepCompareEffect
hook would be the best way to solve the problem, but that would probably only be a slight improvement over what I'm already doing, and since it introduces an external dependency (albeit a small one), I think it might be better suited to a separate PR.
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, I think this is good enough, and we're just refactoring it to hooks anyway. 👍
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.
LGTM 👍 , not tested though. I guess it should be fine as long as the tests are passing?
I went ahead and did a quick test again and it seems like everything is working as intended. @fabiankaegy You need to dismiss your review or switch to an approving review before I can merge. For some reason I can't find a way to do the former. Once this is merged, #28289 can be rebased and will be a lot smaller and easier to review, which should hopefully help it to land in core sooner. |
@fabiankaegy The problem with this change is that if you change a prop (block option) during the loading stage (while the spinner is showing), the entire content of the block disappears. Beforebefore.mp4Afterafter.mp4FYI: You'll notice in the "after" screen recording that the layout of the block changes during loading. This is because I have applied the |
Description
Part of #22890. This PR refactors the
ServerSideRender
component into a function component using React hooks.Getting this merged will make future changes like #28289 easier to land.
How has this been tested?
I inserted an Archives block (which uses
ServerSideRender
) and verified that changing the options in the toolbar and inspector updated the rendered block in the editor.Checklist: