afterChange Hook Only Executes for the Active Locale When Publishing Changes Across Multiple Locales
Describe the Bug
Description:
When publishing changes to a multi-locale document in Payload CMS, the afterChange hook is triggered only for the active locale (the locale open in the CMS interface where the “Publish Changes” button is clicked). This behavior occurs even though publishing updates all locales that have pending draft changes.
Expected Behavior:
The afterChange hook should execute for all locales that are updated during the publish action, ensuring that all relevant locales are processed (e.g., for revalidation or other post-change actions).
Current Behavior:
- The afterChange hook runs exclusively for the locale open in the CMS interface.
- Locales with updated content do not trigger the afterChange hook, potentially leaving them unprocessed (e.g., not revalidated or other necessary actions not performed).
Additional Context:
A workaround could involve looping through all locales in the afterChange hook, but this is inefficient and risks triggering unnecessary actions for locales that were not updated. A more optimal solution would be for the afterChange hook to automatically execute for all updated locales during the publish action.
Link to the code that reproduces this issue
https://github.com/Collexi-Dev/test-payload-3-stable
Reproduction Steps
The reproduction repo is just the current Payload template with added localization and logging in the afterChange hook.
- Create a multi-locale document (e.g., EN, NL).
- Make changes to both EN and NL locales and leave them as drafts.
- Open the NL locale in the CMS interface.
- Click “Publish Changes” in the NL locale.
- Observe that while the changes for both EN and NL are published, the afterChange hook is only triggered for the NL locale.
Which area(s) are affected? (Select all that apply)
area: core
Environment Info
Binaries:
Node: 20.13.1
npm: 10.5.2
Yarn: 1.22.22
pnpm: 9.12.2
Relevant Packages:
payload: 3.0.0
next: 15.0.0
@payloadcms/db-mongodb: 3.0.0
@payloadcms/email-nodemailer: 3.0.0
@payloadcms/graphql: 3.0.0
@payloadcms/live-preview: 3.0.0
@payloadcms/live-preview-react: 3.0.0
@payloadcms/next/utilities: 3.0.0
@payloadcms/payload-cloud: 3.0.0
@payloadcms/plugin-form-builder: 3.0.0
@payloadcms/plugin-nested-docs: 3.0.0
@payloadcms/plugin-redirects: 3.0.0
@payloadcms/plugin-search: 3.0.0
@payloadcms/plugin-seo: 3.0.0
@payloadcms/richtext-lexical: 3.0.0
@payloadcms/translations: 3.0.0
@payloadcms/ui/shared: 3.0.0
react: 19.0.0-rc-65a56d0e-20241020
react-dom: 19.0.0-rc-65a56d0e-20241020
Operating System:
Platform: darwin
Arch: arm64
Version: Darwin Kernel Version 24.0.0: Mon Aug 12 20:52:41 PDT 2024; root:xnu-11215.1.10~2/RELEASE_ARM64_T6031
Available memory (MB): 36864
Available CPU cores: 14
Ran into this same issue today.
After talking this over with the team, we think that this is something users will need to implement on their own.
So in your afterChange hook, you would look for the _status property to switch from draft to published and then you would query each locale document and call the function you need to perform against each one.
Does that make sense? Let me know if I can help any further!
After talking this over with the team, we think that this is something users will need to implement on their own.
So in your afterChange hook, you would look for the
_statusproperty to switch fromdrafttopublishedand then you would query each locale document and call the function you need to perform against each one.Does that make sense? Let me know if I can help any further!
@JarrodMFlesch Do you mean simply looping over all locales and triggering the code inside afterChange for each, unrelated to if the doc was actually updated or not? That's the workaround described in Additional Context and exactly the thing I wanted to avoid here, to unnecessarily trigger code for locales that aren't updated.
I'm not entirely sure why this wouldn't be seen as an actual bug though, you define hooks to execute code during specific events in the lifecycle. When you make changes across all locales and then publish all of them, all of the docs trigger an update and change from draft to published hence this should trigger the afterChange?
If this is not expected behaviour and we should loop over all locales every time the afterChange hook finds a draft that's published then I believe the docs should be updated to reflect that i.e. afterChange is only executed for the open document/locale when publishing.
Not sure if I'm missing something.
@JarrodMFlesch could you please elaborate on the reasoning behind the teams decision?
This issue has been automatically locked. Please open a new issue if this issue persists with any additional detail.
Hi @Collexi-Dev @rilrom, I understand and agree that the behavior here is very confusing.
To the best of my understanding, this issue is due to the flow of our publishing process. This is currently what is happening:
- Publish all is selected
- The current document is updated and runs all hooks
- For others locales, document data gets overwritten with
_status: published-> here we directly overwrite the data because we do not want all the hooks to run -> at this point in the process, we do not know what the receiving locale data looks like (i.e. what if the other locale was already published and therefore no data changed?) and then it is not possible to determine whether theafterChangeshould be run
Our dev team reinvestigated this issue and unfortunately there is no ideal or proposed solution yet. There were a couple approaches discussed but unfortunately it would either incur a large performance penalty or cause breaking changes in regards to how data is handled.
Until we land on a resolution, here are some things you could try:
- As @JarrodMFlesch mentioned, you could expand your
afterChangeto fetch your other locale documents, compare the data and run your logic accordingly. - Similarly to how our
publishSpecificLocalebutton works, you could add a custom Publish button which loops all locales and publishes them consecutively.
If you have any thoughts or suggestions, we would be happy to hear them. I apologize for the situation and appreciate the detailed issue here!
Closing as not planned for now - planned to revisit fix in the future.