Skip to content
This repository has been archived by the owner on Jul 28, 2023. It is now read-only.

Reuse the same RichText component across the different environments (Edit, Save and Frontend) #2

Merged
merged 5 commits into from
Apr 4, 2022

Conversation

fabiankaegy
Copy link
Member

The Title component now gets used both in the editor and on the frontend to output the correct markup for either a contenteditable area or the rendered component.

The Title Component gets used both in the editor and on the frontend to output the correct markup for either a contenteditable area or the rendered component
@luisherranz
Copy link
Member

Thanks for the PR, Fabian 🙂

Even though this technically works, we can't import @wordpress/block-editor in the frontend because we went from 390Kb (which is already terrible and needs to be improved) to 1.7Mb (both uncompressed).

I agree that having a context-aware version of primitive components like RichText would be really interesting, but we need to find a way to do it so that it doesn't end up including the whole Gutenberg codebase 😄

The current WordPress dependency system is based on globals, and not tree-shakable friendly. We would probably need to:

  • Get rid of the WordPress globals (at least in the frontend).
  • Make WordPress packages tree-shake-friendly.
  • Use some kind of standard environment variable to facilitate tree-shaking, similar to what packages do with NODE_ENV:
    export const RichText = process.env.WORDPRESS_CONTEXT === "editor" ? FullRichText : SlimRichText

Maybe we could try to do accomplish it first here in isolation, with a fake RichText created ad-hoc, and see if we can tree-shake it correctly?

I'm not an expert in tree-shaking, to be honest, so we may learn some things 🙂

@fabiankaegy
Copy link
Member Author

Yeah completely agree on the size here (y) I think this current state is the proof of concept of what I would love the user experience and developer experience of hydrated blocks to be like.

And yeah the three goals you have outlined are spot on.

This here is all the RichText.Content component does:
https://github.com/WordPress/gutenberg/blob/16b89d66a0091213bfbc6fad5a0db66fa6589a62/packages/block-editor/src/components/rich-text/index.js#L428-L452

So I think it should be feasible to create a shim for that for the frontend to ressolve some of these pains

@luisherranz
Copy link
Member

But you don't even need that because the RichText.Content code is used to serialize the data from the editor (RichText). In the frontend, the HTML from RichText is already serialized. Or do you have another use case in mind?

@fabiankaegy
Copy link
Member Author

🤦 Yeah, you are right 👍 I cannot think of other use cases.

@luisherranz
Copy link
Member

Still, it would be really interesting to reuse components like RichText seamlessly across contexts (edit, save, frontend) 🙂

@fabiankaegy
Copy link
Member Author

The commit I just pushed adds a shim for RichText.Content for the frontend but isn't jet super nice because we need to somehow make sure the editor loads the blockEditor script before our editor script gets executed.

I've solved that for now with an unused import in the edit.js file but the developer experience of that isn't great.

@luisherranz
Copy link
Member

That's a nice trick 😄

But yes, it can easily break if devs forget or accidentally delete the "unrelated" import, as you mentioned.

Another idea could be to use different entry points. For example:

  • @wordpress/block-editor for the Edit
  • @wordpress/block-editor/save for the Save
  • @wordpress/block-editor/frontend for the Frontend

In the packages, add the correct sideEffects configuration and export the different versions of each primitive.

Then, use different bundling aliases for each context:

// Config for Save bundle
{
  "resolve": {
    "alias": {
      "@wordpress/block": "@wordpress/block/save",
    }
  }
}
// Config for Frontend bundle
{
  "resolve": {
    "alias": {
      "@wordpress/block": "@wordpress/block/frontend",
    }
  }
}

This approach could also work with import maps:

<script type="importmap">
{
  "imports": {
    "@wordpress/block": "https://cdn.skypack.dev/@wordpress/block@^X.X.X/frontend",
  }
}
</script>

Both approaches (env variable or alias) require configuration. But I guess people are relying more and more on wp-scripts nowadays, aren't they?

@luisherranz luisherranz changed the title fix replace TextControl in editor with RichText Reuse the same RichText component across the different environments (Edit, Save and Frontend) Apr 4, 2022
@luisherranz
Copy link
Member

I've asked if there's a way to distinguish between Edit and Save in Slack.

>
<Children
value={innerBlocks && innerBlocks.innerHTML}
<EnvContext.Provider value="frontend">
Copy link
Member

@luisherranz luisherranz Apr 4, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally, I think we could add one provider to each environment like this one with the correct value, so users only have to do this on their code:

const env = useContext(EnvContext); // "edit", "save" or "frontend"

or even expose a hook:

const env = useBlockEnv(); // "edit", "save" or "frontend"

@luisherranz
Copy link
Member

I've used a useBlockEnvironment hook to make RichText even more transparent.

@fabiankaegy let me know what you think!


export const RichText = ({ tagName: Tag, children, ...props }) => {
return useBlockEnvironment() === "edit" ? (
<window.wp.blockEditor.RichText value={children} tagName={Tag} {...props} />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still using your trick to solve the bundling issue. We can explore solutions to this in a different PR.

@luisherranz
Copy link
Member

@Mamaduka suggested us in Slack to explore useBlockEditContext as well, which returns null in the Save components.

@fabiankaegy
Copy link
Member Author

@luisherranz thinking about it more using the useBlockEditorContext likely won't work because it itself is a wp global dependency... This makes me think that likely the best long-term solution is either a solution for tree shakable WordPress globals on core, or a new package that is meant to be used on the frontend also that is built with size/frontend performance in mind that exposes such a new hook in core.

@fabiankaegy
Copy link
Member Author

But outside of that hook I think this is in a great spot to get merged in and refined in follow up PR's

@luisherranz
Copy link
Member

Perfect. Merging this 🙂 👏

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants