-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
Add callbacks to ServerSideRenderer to handle failures with custom renderers #16512
Add callbacks to ServerSideRenderer to handle failures with custom renderers #16512
Conversation
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.
Thank you for your contribution @mikejolley 👍 Generally, it seems a good idea to add options to configure empty/error states.
if ( response === '' ) { | ||
return ( | ||
return !! onEmptyResponse ? onEmptyResponse( 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.
Can onEmptyResponse be just a react element, instead of a function ? e.g: !! onEmptyResponse ? onEmptyResponse : (
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.
In the RSS Block we want to disable the block settings, if we get an error from the SSR. So I think a function is more flexible.
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 also thought function would be more flexible but open to change if it's required to get this through. Worth mentioning, the code using this component has no access to this components state without a function (passing this
) which may be important, or not :)
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.
The case @Soean provided is interesting:
In the RSS Block we want to disable the block settings.
We call the function onEmptyResponse on the render phase, render can not have side effects. So, for example, if there is a need to change the local state when the response is empty, that's not possible.
I guess to disable block settings on empty response, we would need to change a local state flag, so this change would not fit the RSS block use case.
I guess needing side effects when the response is empty will be common.
Maybe a possibility that would fit the use case would be an onEmptyResponse event. This function would be called, after the fetch is complete inside the fetch function, with an event object we define as a parameter. As a response to this event, users can apply any side effect desired e.g.: change state. This function would not render anything.
For the render, we have some possibilities:
- have a prop emptyResponse that is rendered when the response is empty.
- render nothing if onEmptyResponse is passed, the parent as a response to this event would be responsible for rendering the UI needed.
Any thoughts on this @mikejolley, @Soean?
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've no thoughts on this one as my use case didn't require changing state, just acting on the state the block already had.
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.
In looking at the comments here I like some of what @jorgefilipecosta proposes. Here's what I'd like to see that I think will satisfy potential requirements for handling side-effects and a custom empty response render
- Instead of an empty response event callback fired on completed fetch (as suggested by Jorge), I think it may be more extensible to simply fire any
fetchComplete
callback that is provided in props. This opens up the possibility for components to trigger side-effects on various fetch states (empty response, successful response, caught fetch error). This could be available for more advanced use-cases. - Just accept render props for the different response state props (and since these are placeholders, let's explicitly call them that)
EmptyResponsePlaceholder
,NullResponsePlaceholder
andErrorResponsePlaceholder
options here and feed any state to them as they are rendererd. You can then have default values for the render props (which could simplify some of the conditionals here).
if ( response === '' ) { | ||
return ( | ||
return !! onEmptyResponse ? onEmptyResponse( this ) : ( | ||
<Placeholder | ||
className={ className } | ||
> | ||
{ __( 'Block rendered as empty.' ) } |
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 wonder if onEmptyResponse should be rendered inside the placeholder? The advantage is that it is easier to use, one does not need to pass the placeholder. The disadvantage is that it is not as customizable.
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 did it this way so we can pass a custom classname to Placeholder, or allow flexibility to use something completely different.
<Placeholder | ||
className={ className } | ||
> | ||
{ __( 'Block rendered as empty.' ) } | ||
</Placeholder> | ||
); | ||
} else if ( ! response ) { | ||
return ( | ||
return !! onNullResponse ? onNullResponse( 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.
What is the relevant use case for a null response? Can onEmptyResponse handle all falsy values?
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.
Looking at the code, it seems the ! response
is ran when response is null. This seems to happen when things have not yet loaded? It shows a spinner. I was thinking by allowing each of these to have their own callback you could render a custom loading state too.
Does this need revising @jorgefilipecosta ?
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.
Ya it looks like null is used to indicate a "loading" state. So there's value in leaving it as is. However, I would suggest a slight name change here to make the public exposed api a bit clearer. So instead of NullResponse
for the render prop (continuing my earlier comment for the review), maybe make it LoadingResponsePlaceholder
.
@nerrad @jorgefilipecosta I've updated this with the suggested callbacks:
The defaultProps handle the default placeholders which keeps the code in render() clean. Let me know if this is looking better. I've also updated the PR in Woo which is using this (woocommerce/woocommerce-blocks#717). |
{ __( 'Block rendered as empty.' ) } | ||
</Placeholder> | ||
); | ||
return emptyResponsePlaceholder( this.props, response ); |
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 this should be a callback. Just make it a render prop. So consumers would provide the component they want rendered here and it would be implemented via:
<emptyResponsePlaceholder response={ response } { ...this.props } />
or more conventionally via (which is more similar to what you have):
emptyResponsePlaceholder( { ...this.props, response } );
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.
Take a look now - I just made this change. I renamed the props because the component names required a capital.
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 👍 cc @jorgefilipecosta can you give this another looksee too?
677e495
to
aa339de
Compare
…nderers (WordPress#16512) * Add callbacks to ServerSideRenderer to handle failures * Update variable naming and set default renderers * LoadingResponsePlaceholder * swtich case * fetchComplete * Remove fetchcomplete method * Pass render props * add changelog entry * add documentation for new props
…nderers (#16512) * Add callbacks to ServerSideRenderer to handle failures * Update variable naming and set default renderers * LoadingResponsePlaceholder * swtich case * fetchComplete * Remove fetchcomplete method * Pass render props * add changelog entry * add documentation for new props
…nderers (#16512) * Add callbacks to ServerSideRenderer to handle failures * Update variable naming and set default renderers * LoadingResponsePlaceholder * swtich case * fetchComplete * Remove fetchcomplete method * Pass render props * add changelog entry * add documentation for new props
Description
Added 3 optional callbacks to the
ServerSideRender
component so that consumers can change what is displayed based on the result. These include:-> emptyResponsePlaceholderonEmptyResponse
-> errorResponsePlaceholderonErrorResponse
-> loadingResponsePlaceholderonNullResponse
How has this been tested?
Testing this with my own code/callback function.
And my callback:
Screenshots
The above example let's me render this when my block renderer returns empty content:
The default state is left alone if you don't pass a callback:
Types of changes
This adds some non-required props to
<ServerSideRender>
so it shouldn't impact existing code.Checklist: