-
Notifications
You must be signed in to change notification settings - Fork 4.5k
ServerSideRender: refactor fetchData to use useCallback
and refs
#69237
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
ServerSideRender: refactor fetchData to use useCallback
and refs
#69237
Conversation
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.
To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
Hi @t-hamano, Could you please test this PR and share your feedback on the approach? I tested it on the Looking forward to your thoughts! |
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.
Looks good to me, I do wonder why the previous implementation skipped on useCallback 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.
Thanks for the PR!
This approach seems good to me, but I wonder why this issue occurs only for certain attributes. Do you know anything about this?
For example, in the Without this PR, changes to a In the case of
To summarize, the P.S. All of this can be verified by simply looking at the classes applied on the |
dc4e40c
to
022c0c1
Compare
I understand this point.
For example, if you change the setting of "Display avatar" in the Latest Comment block, |
When updating color, the props passed to SSR change twice, whereas toggling Display Avatar causes only a single change. This can be verified by logging them within an effect inside the SSR component. When the props change twice, the debouce gets cancelled. (often, the multiple changes do not have different data) After tracing the render flow, I can confirm that foundational components like the color panel and respective palettes don’t cause redundant changes to the attributes post execution of The issue arises from how some blocks use For example:
attributes={ { ...attributes, ...getYearMonth( date ) } } This spreads a new object every time on every render. Passing
Both It’s unclear why Display Avatar doesn’t trigger the same behavior as guessing which attribute causes re-rendering and under which circumstances is quite challenging. I think it's best to solve it with the currently proposed |
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.
Thanks for the detailed explanation! I think your explanation makes sense.
Two suggestions before shipping:
- Do we really need to use
useEffect
hook? Can we simplify the code to the following?diff --git a/packages/server-side-render/src/server-side-render.js b/packages/server-side-render/src/server-side-render.js index d5885ec318..2c6ed55325 100644 --- a/packages/server-side-render/src/server-side-render.js +++ b/packages/server-side-render/src/server-side-render.js @@ -107,10 +107,7 @@ export default function ServerSideRender( props ) { const [ isLoading, setIsLoading ] = useState( false ); const latestPropsRef = useRef( props ); - // Store props in ref to prevent stale closures in the fetchData callback. - useEffect( () => { - latestPropsRef.current = props; - }, [ props ] ); + latestPropsRef.current = props; const fetchData = useCallback( () => { if ( ! isMountedRef.current ) {
- This package is primitive and rarely updated. It might be a good idea to update the CHANGELOG to highlight this bug fix.
022c0c1
to
d1c5193
Compare
Thanks for the review, @t-hamano.
You're right! The |
Side note: using (layout) effect to write refs is a known pattern. It's also recommended to avoid reading or writing refs during render. See: |
Apologies for missing it, and thanks for pointing that out. I'll be creating a follow-up PR to fix this ASAP. |
…ordPress#69237) * refactor fetchData to use `useCallback` and `refs` * refactor: use `latestPropsRef` directly * fix: remove redundant `useEffect` * chore: add `CHANGELOG` Co-authored-by: yogeshbhutkar <yogeshbhutkar@git.wordpress.org> Co-authored-by: Mayank-Tripathi32 <mayanktripathi32@git.wordpress.org> Co-authored-by: t-hamano <wildworks@git.wordpress.org>
What?
Closes #69220
The
ServerSideRender
does not re-render when the attributes change. This issue can be best reproduced with theCalendar
block where theBackground
property does not reflect on the editor upon making changes.Why and How?
According to the
useDebounce
implementation, if any of the arguments change, including the function to debounce, the scheduled call gets canceled.Ref:
gutenberg/packages/compose/src/hooks/use-debounce/index.js
Line 36 in c856964
In our case, the
fetchData
function changes. The debounce function should ideally be wrapped within auseCallback
as recommended here:gutenberg/packages/compose/src/hooks/use-debounce/index.js
Lines 17 to 20 in c856964
This alone won't suffice as the change in
props
causes thefetchData
to re-create/change again. It's best to mimic the props with refs which should be used withinfetchData
with an empty dependency array. The ref will act as a "pointer" to current props without causing function recreation and this approach won't cancel the debounce calls as reflected in the screencast below.Testing Instructions
Note: A similar issue involving the
Latest Comments
block is also fixed.Screencast
calendar-fix.mov