/** * External dependencies */ import { isEqual, debounce } from 'lodash'; /** * WordPress dependencies */ import { Component, RawHTML, Fragment } from '@wordpress/element'; import { __, sprintf } from '@wordpress/i18n'; import apiFetch from '@wordpress/api-fetch'; import { addQueryArgs } from '@wordpress/url'; import { Placeholder, Spinner } from '@wordpress/components'; export function rendererPath(block, attributes = null, urlQueryArgs = {}) { return addQueryArgs(`/wp/v2/block-renderer/${block}`, { context: 'edit', ...(null !== attributes ? { attributes } : {}), ...urlQueryArgs, }); } export class TagGroupsServerSideRender extends Component { constructor(props) { super(props); this.state = { response: null, prevResponse: null, }; } componentDidMount() { this.isStillMounted = true; this.fetch(this.props); // Only debounce once the initial fetch occurs to ensure that the first // renders show data as soon as possible. this.fetch = debounce(this.fetch, 500); } componentWillUnmount() { this.isStillMounted = false; } componentDidUpdate(prevProps) { if (!isEqual(prevProps.attributes, this.props.attributes)) { this.fetch(this.props); } } fetch(props) { if (!this.isStillMounted) { return; } if (null !== this.state.response) { this.setState({ response: null, prevResponse: this.state.response }); } const { block, attributes = null, urlQueryArgs = {}, onFetched } = props; const path = rendererPath(block, attributes, urlQueryArgs); // Store the latest fetch request so that when we process it, we can // check if it is the current request, to avoid race conditions on slow networks. const fetchRequest = (this.currentFetchRequest = apiFetch({ path }) .then((response) => { if ( this.isStillMounted && fetchRequest === this.currentFetchRequest && response ) { this.setState({ response: response.rendered }); if (props['onFetched']) { props['onFetched'](); } } }) .catch((error) => { if (this.isStillMounted && fetchRequest === this.currentFetchRequest) { this.setState({ response: { error: true, errorMsg: error.message, }, }); } })); return fetchRequest; } render() { // inspiration from https://github.com/dgwyer/server-side-render-x/blob/master/server-side-render-x.js const { right, top, unit } = this.props.spinnerLocation; const response = this.state.response; const prevResponse = this.state.prevResponse; const { className, EmptyResponsePlaceholder, ErrorResponsePlaceholder, LoadingResponsePlaceholder, } = this.props; if (response === '') { return ; } else if (!response) { return ( ); } else if (response.error) { return ; } return ( {response} ); } } TagGroupsServerSideRender.defaultProps = { spinnerLocation: { right: 0, top: 10, unit: 'px' }, EmptyResponsePlaceholder: ({ className }) => ( {__('Block rendered as empty.')} ), ErrorResponsePlaceholder: ({ response, className }) => { const errorMessage = sprintf( // translators: %s: error message describing the problem __('Error loading block: %s'), response.errorMsg ); return {errorMessage}; }, LoadingResponsePlaceholder: ({ className, top, right, unit, prevResponse, }) => { return (
{prevResponse}
); }, }; export default TagGroupsServerSideRender;