I am working on a custom block type for the Wordpress Gutemberg editor.
The block type displays and saves content and a date.
I'm trying to save the creation date for every block by setting a postDate value on the block type attributes. And I could successfully save this value using the save function.
However, the value is not updated on every block creation, it stays the same and every block has the same postDate value (the creation of the first block) passed to the database.
I need it to be updated so it can be saved in the "save" function. It needs to be updated only once, when a block is created.
This is the workflow I'm trying to achieve:
User creates a block / user edits the block / edits and hits the "Update" button (preferably the first option) -> postDate for that block is saved only once
User creates a different block -> same process occurs. Each block has its postDate value saved. The value can not be altered by subsequent edits
here is the function:
registerBlockType(metadata.name, {
attributes: {
content: {
type: "string",
source: "html",
selector: "h2",
},
postDate: {
type: "string",
default: moment().tz("Europe/Paris").locale("fr").format(),
},
blockId: {
type: "string",
},
},
edit({ attributes, setAttributes, clientId }) {
const blockProps = useBlockProps();
const { postDate, blockId } = attributes;
React.useEffect(() => {
if (!blockId) {
setAttributes({ blockId: clientId });
}
}, []);
const TEMPLATE = [["core/paragraph", {}]];
const ALLOWED_BLOCKS = useMemo(
() =>
wp.blocks
.getBlockTypes()
.filter((block) => block.name !== metadata.name)
.map((block) => block.name),
[metadata.name]
);
return (
<div className="wp-post-live-block">
<time dateTime={postDate}>
{moment(postDate).format("D/M H:mm:ss")}
</time>
<RichText
{...blockProps}
tagName="h2"
value={attributes.content}
onChange={(content) => setAttributes({ content })}
placeholder={__("Add title")}
/>
<InnerBlocks template={TEMPLATE} allowedBlocks={ALLOWED_BLOCKS} />
</div>
);
},
save({ attributes }) {
const blockProps = useBlockProps.save();
const { postDate, blockId } = attributes;
return (
<div data-timestamp={postDate} id={blockId} className="wp-post-live-block">
<RichText.Content
{...blockProps}
tagName="h2"
value={attributes.content}
/>
<InnerBlocks.Content />
</div>
);
},
});
I've tried setting the postDate value on the useEffect tool, inside the if (!blockId) condition. However, this makes the function try to update the value on every render.
Where can I hook the setAttribute(postDate) so that it updates only once, when the block is created?
Your code is actually very close! The postDate
attribute should be a safe default "empty string" for the datetime, not the current time. By using an empty string, the edit()
function can determine if a date was previously set and if not, set it.
The second part is the save()
function, where you have the data-timestamp
for the timestamp, but this is not properly configured to the postDate
attribute. By setting the correct selector and attribute for postDate
, the saved timestamp will be used and not recreated each time, eg:
JS
registerBlockType(metadata.name, {
attributes: {
content: {
type: "string",
source: "html",
selector: "h2",
},
postDate: {
type: "string",
source: "attribute",
selector: "div",
attribute: "data-timestamp", // source of datetime value
default: "" // safe default
}
// Removed blockId
},
edit({ attributes, setAttributes, clientId }) {
const blockProps = useBlockProps();
const { postDate, content } = attributes;
React.useEffect(() => {
// Default is empty string, so the time will be set when block is added
if (postDate === "") {
setAttributes({ postDate: moment().tz("Europe/Paris").locale("fr").format() });
}
}, [postDate]); // Watch postDate, which after being saved, won't be empty so will be initial time block was added
...
return (
<div {...blockProps}>
<time dateTime={postDate}>
{moment(postDate).format("D/M H:mm:ss")}
</time>
<RichText
tagName="h2"
value={attributes.content}
onChange={(content) => setAttributes({ content })}
placeholder={__("Add title")}
/>
...
</div>
);
},
save({ attributes }) {
const blockProps = useBlockProps.save({className: 'wp-post-live-block'}); // Add className to blockProps
const { postDate, content } = attributes;
return (
<div data-timestamp={postDate} {...blockProps}>
<RichText.Content
tagName="h2"
value={content}
/>
...
</div>
);
},
});
NB. Removed the InnerBlocks related code unrelated to the issue so the changes to enable datetime to work are easier to see.