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
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions src/edit.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
import { TextControl } from "@wordpress/components";
// This import is needed to ensure that the `wp.blockEditor` global is available
// by the time this component gets loaded. The `Title` component consumes the
// global but cannot import it because it shouldn't be loaded on the frontend of
// the site.
import "@wordpress/block-editor";

import { useBlockProps, InnerBlocks } from "@wordpress/block-editor";
import Title from "./frontend/title";

export default function Edit({ attributes, setAttributes }) {
const blockProps = useBlockProps();
return (
<>
<div {...blockProps}>
<TextControl
placeholder="Enter the title"
<Title
value={attributes.message}
onChange={(val) => setAttributes({ message: val })}
placeholder="Enter the Title"
/>
<InnerBlocks />
</div>
Expand Down
22 changes: 12 additions & 10 deletions src/framework/view.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { hydrate } from "./wordpress-element";
import { hydrate, EnvContext } from "./wordpress-element";

const blockTypes = new Map();

Expand Down Expand Up @@ -30,16 +30,18 @@ class GutenbergBlock extends HTMLElement {
const innerBlocks = this.querySelector("gutenberg-inner-blocks");
const Comp = blockTypes.get(blockType);
hydrate(
<Comp
attributes={attributes}
blockProps={blockProps}
suppressHydrationWarning={true}
>
<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"

<Comp
attributes={attributes}
blockProps={blockProps}
suppressHydrationWarning={true}
/>
</Comp>,
>
<Children
value={innerBlocks && innerBlocks.innerHTML}
suppressHydrationWarning={true}
/>
</Comp>
</EnvContext.Provider>,
this
);
});
Expand Down
9 changes: 9 additions & 0 deletions src/framework/wordpress-blockeditor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { useBlockEnvironment } from "./wordpress-element";

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.

) : (
<Tag {...props}>{children}</Tag>
);
};
30 changes: 26 additions & 4 deletions src/framework/wordpress-element.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,36 @@
import {
useState as useReactState,
useEffect as useReactEffect,
useContext as useReactContext,
createContext,
} from "@wordpress/element";
export { hydrate } from "react-dom";

// Dirty dirty trick
export const isView = !window.wp.blockEditor;
export const EnvContext = createContext(null);

/**
* A React hook that returns the name of the environment.
*
* This is still a bit hacky. Ideally, Save components should support React
* hooks and all the environments (Edit, Save and Frontend) should populate a
* normal context. Also, more environments could be added in the future.
*
* @returns {"edit" | "save" | "frontend"}
*/
export const useBlockEnvironment = () => {
try {
const env = useReactContext(EnvContext);
if (env === "frontend") return "frontend";
return "edit";
} catch (e) {
return "save";
}
};

const noop = () => {};

export const useState = (init) => (isView ? useReactState(init) : [init, noop]);
export const useState = (init) =>
useBlockEnvironment() !== "save" ? useReactState(init) : [init, noop];

export const useEffect = (...args) => (isView ? useReactEffect(...args) : noop);
export const useEffect = (...args) =>
useBlockEnvironment() !== "save" ? useReactEffect(...args) : noop;
8 changes: 7 additions & 1 deletion src/frontend/title.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
const Title = ({ message }) => <h2 className="title">{message}</h2>;
import { RichText } from "../framework/wordpress-blockeditor";

const Title = ({ message, ...props }) => (
<RichText tagName="h2" className="title" {...props}>
{message}
</RichText>
);

export default Title;