Open pryley opened 3 years ago
@jasmussen since you worked on https://github.com/WordPress/gutenberg/issues/35027, perhaps you'd be open to looking at this since it's related to the SSR?
I'm currently using a custom ServerSideRender class which is identical to the latest commit with the loading indicator changes except that it also includes the change above...but I'd love to see this or something similar in core.
@pryley What's the use case for such a callback?
@talldan In my case I use it to initialise a carousel layout, and to transform a SELECT into a custom rating control in a form. Additionally, I use it to add a direction (i.e. ltr, rtl) class which determines how the block's CSS layout is displayed.
As mentioned in the description, the same thing had been brought up in https://github.com/WordPress/gutenberg/issues/7346. I don't see why not adding this feature, but maybe @gziolo has some more insights?
As for the actual implementation, I'm not sure onRender
is the right API to use here. I would probably create a useServerSideRender
hook instead and use that to implement <SeverSideRender>
under the hood. So if you want to run some side effects between renders, use the hook instead of the component for more controls.
const html = useServerSideRender( block, attributes );
useEffect( () => {
// Do your things.
}, [ html ] );
return <RawHTML>{ html }</RawHTML>;
Thanks for sharing @pryley. I like @kevin940726's idea.
@kevin940726 @talldan Thanks, that sounds promising! However, I don't quite understand how the useServerSideRender
hook would be implemented as described.
This is my current implementation:
const edit = props => {
const { attributes: { /* ... */ }, className, setAttributes } = props;
const inspectorControls = { /* ... */ }
const inspectorAdvancedControls = { /* ... */ }
return [
<InspectorControls>
<PanelBody title={ _x('Settings', 'admin-text', 'site-reviews')}>
{ Object.values(wp.hooks.applyFilters('site-reviews.form.InspectorControls', inspectorControls, props)) }
</PanelBody>
</InspectorControls>,
<InspectorAdvancedControls>
{ Object.values(wp.hooks.applyFilters('site-reviews.form.InspectorAdvancedControls', inspectorAdvancedControls, props)) }
</InspectorAdvancedControls>,
<ServerSideRender block={ blockName } attributes={ props.attributes }></ServerSideRender>
];
};
This is what I tried which obviously doesn't work since the hook is fired before render instead of before/after render:
const edit = props => {
const { attributes: { /* ... */ }, className, setAttributes } = props;
const inspectorControls = { /* ... */ }
const inspectorAdvancedControls = { /* ... */ }
const rendered = (<ServerSideRender block={ blockName } attributes={ props.attributes }></ServerSideRender>)
useEffect(() => {
console.log(rendered);
}, [rendered]);
return [
<InspectorControls>
<PanelBody title={ _x('Settings', 'admin-text', 'site-reviews')}>
{ Object.values(wp.hooks.applyFilters('site-reviews.form.InspectorControls', inspectorControls, props)) }
</PanelBody>
</InspectorControls>,
<InspectorAdvancedControls>
{ Object.values(wp.hooks.applyFilters('site-reviews.form.InspectorAdvancedControls', inspectorAdvancedControls, props)) }
</InspectorAdvancedControls>,
<RawHTML>{ rendered }</RawHTML>
];
};
Would you be able to provide some more details on the implementation as described?
Although perhaps I have misunderstood, and when referring to a useServerSideRender
hook you mean the actual implementation inside ServerSideRender
?
I think @kevin940726 was referring to a new API that would be implemented in Gutenberg (as an alternative to having a callback prop).
So this is still not a thing right? I tried finding any info on this or something equal for an hour no but nothing.
How would one go about triggering custom JS functionality after a successful serversiderender?
Do I have to use componentDidUpdate()
?
@jonathan-dejong
I assume this isn't a priority since the Gutenberg focus has been to move away from server-side rendering.
I get around this by using a custom ServerSideRender with the following code added:
useEffect(() => {
if (props.onRender) {
props.onRender(response, block, attributes);
}
}, [response]);
The onRender (GLSR.Event is the event handler from my plugin Site Reviews):
const onRender = (response, block, attributes) => {
GLSR.Event.trigger(block, response, attributes);
}
export default onRender;
Usage:
<ServerSideRender block={ blockName } attributes={ props.attributes } onRender={ onRender }></ServerSideRender>
Thank you for the response @pryley
My impression is that most agencies build only or mostly dynamic blocks so that's a bit off the mark to do imo 😅 Custom static blocks are a horror to maintain and causes all sorts of problems with other things too so we try to build dynamic as well.
what does your ServerSideRender look like?
@jonathan-dejong
In my case, I'm waiting on upgrading to dynamic blocks until the next major version of the plugin. The reason being that dynamic blocks would likely not work with the template system that the plugin uses (i.e. copying plugin template files to your theme to customize the HTML) so it would be a breaking change.
You can see the current (somewhat outdated) implementation here:
that's a nice simple approach yeah.
I don't see why that couldn't be added to the component already.
I'm hoping it might help someone. I am using MutationObserver in my code for such thing, it signals me that I need to update the data in the block using js. Below is the code.
const Edit = ( props ) => {
const { attributes, setAttributes } = props;
...
...
const memoizedSSR = useMemo( () => {
return (
<ServerSideRender
block = { block.name }
attributes = { attributes }
className = 'myblock'
/>
)
}, [ attributes ] );
const ref = useRefEffect( ( block ) => {
const observer = new MutationObserver((mutations, observer) => {
for(let mutation of mutations) {
for (let node of mutation.addedNodes) {
if (node.tagName == 'DIV' && node.classList.contains('myblock')) {
console.log('do things with pure js library...');
}
}
}
});
observer.observe(block, { childList: true, subtree: false } );
return () => {
observer.disconnect();
}
}, [] );
...
...
return (
<>
<InspectorControls...>
<BlockControls...>
<div { ...useBlockProps( { ref } ) }>
{ memoizedSSR }
</div>
</>
);
}
export default Edit;
@yalogica Your approach works perfectly! Thank you for sharing 🤩
What problem does this address?
Sometimes you need to trigger some javascript after a block is rendered with
ServerSideRender
.Previously, the solution was to extend the ServerSideRender class. However with WordPress 5.8 this is no longer easily possible due to the changes made to allow Blocks to work outside of the post editor (i.e. sidebars and widgets).
See also:
What is your proposed solution?
My suggestion is to include a
onRender
prop toServerSideRender
and trigger it with theuseEffect
hook:And this is how you would use it in your block: