Blocks, Post meta and the Query Loop
When I was working on the CNHSA site, I couldn’t get MediaPress to wire up postmeta for whatever reason and I had already had the fields I wanted registered with ACF so I wrote a few hooks to get the data I needed to create a dyamic “address” block. Then, I tried using it inside a query loop block, and that failed.
After a little digging, here’s how to write a block that is compatible with the query loop.
The Data
The query loop block provides the following context:
"providesContext": {
"queryId": "queryId",
"query": "query",
"displayLayout": "displayLayout",
"enhancedPagination": "enhancedPagination"
},
Your block, then, just needs to consume that context!
In Practice
Here’s how I’m consuming that data in my custom useAcf
hook.
// ./hooks/useAcf.tsx file
import { useState, useEffect } from '@wordpress/element';
import apiFetch from '@wordpress/api-fetch';
import { useSelect } from '@wordpress/data';
import { store as editorStore } from '@wordpress/editor';
type Context = {
queryId?: number;
postType?: string;
postId?: number;
}
export default function useAcf( context: Context ) {
const [ isLoading, setIsLoading ] = useState( true );
const [ acf, setAcf ] = useState( [] );
const isInQueryLoop = context?.queryId !== undefined;
const [ postId, setPostId ] = useState<string | number | null>( context.postId || null );
const [ postType, setPostType ] = useState<string | null>( context.postType || null );
useSelect( ( select ) => {
if ( ! isInQueryLoop ) {
setPostId( select( editorStore ).getCurrentPostId() );
setPostType( select( editorStore ).getCurrentPostType() );
}
}, [ isInQueryLoop ] );
useEffect( () => {
const controller = typeof AbortController !== 'undefined' ? new AbortController() : null;
setIsLoading( true );
apiFetch( { path: `/wp/v2/${ postType }/${ postId }`, signal: controller?.signal } )
.then( ( postMeta ) => {
setAcf( postMeta.acf );
} ).catch( () => {
setAcf( [] );
} ).finally( () => {
setIsLoading( false );
} );
return () => {
controller?.abort();
setIsLoading( false );
setAcf( [] );
};
}, [ postType, postId ] );
return { acf, isLoading };
}
What’s Happening:
At a high level, here’s what’s going on.
- Import the bits from WordPress
- Define the Context type for TS help
- init your variables with useState, setting them to default either to context’s args or null (if they don’t exist)
- use WordPress’s useSelect to hook to get the data from the editor if we aren’t in the query loop
- use a useEffect hook to fetch the ACF data from the Rest API (simple enough, but this assumes everything is set to
show_in_rest
- Return the ACF data and the
isLoading
state to be handled in the component