Processor cannot handle well `children` and other issues in my demo

I have created a Frontity Demo to show the use of the Intersection Observer useInView

The demo can be seen online at CodeSandbox

Features of the demo:

  • It uses a source processor that extracts the source link of the post and populates it to the state (so then it can be used to load the iFrame)
  • It uses a HeadingInView processor that replaces the <h2>, <h3>, <h4> by the same headings but with Intersection Observer behavior (it displays a message in the console when the heading is in the viewport)
  • It displays an Iframe on each post with the sourcePost property got with the source processor

I have the following issues with this demo

SSR Error Message

Whenever I render a post page from SSR (for example reloading http://localhost:3000/2016/the-beauties-of-gullfoss/) I get the following message error in the console

Warning: Expected server HTML to contain a matching <noscript> in <div>.

CSR Error Message

Whenever I navigate to a post page from CSR (for example the post http://localhost:3000/2016/the-beauties-of-gullfoss/) I get the following message error in the console

Warning: Cannot update a component (`Post`) while rendering a different component (`Html2React`)...

Maybe it’s because the processor finishes its parsing and updates the state while the Post component is being rendered? Is there any way to avoid this error?

Processor cannot handle well children

To replace headings found with themselves but with Intersection Observer behaviour I have used this code

import React, {useEffect} from 'react'
import useInView from "@frontity/hooks/use-in-view";

const WithInViewHeading = (children, headingTagName) => {  
  const childrenContent = children[0].content
  const InnerComponent = () => {
    const { ref, inView } = useInView();
    useEffect(() => {
      if (inView) console.log(`${+new Date()} → Heading In View: ${childrenContent}`)
    });
    
    return React.createElement(headingTagName, {ref}, childrenContent) 
  }
  return InnerComponent
}

const HeadingInView = {
  priority: 10,
  name: "h3-in-view",
  test: ({ node }) => (
    node.component === "h2" || node.component === "h3" || node.component === "h4" && node.children.length
  ),
  processor: ({ node, state }) => {
    node.component = WithInViewHeading(node.children, node.component)
    return node
  },
};

export default HeadingInView;

The main problem with this code is that any heading using child tags (<strong>, <span>, …) won’t work (in fact with this code some headings detected have children as undefined and those heading appear with no content)

I’ve been looking for a better solution but couldn’t find one.
Any ideas on how to properly enhance tags detected in a processor by properly creating the React version with its proper children?


@cristianbote any thoughts on this?

Hey @juanma, so I was digging into this one and the main errors are coming from the fact that by the time you define sourcePost inside the source processor the <Post /> has been rendered. So, that means the server html does not include the iframe element. So, either include the iframe only for state.frontity.rendering === 'csr' or include the iframe at processor level, similar with what you did with the headings.

Something in the lines of:

import React from "react";
import Iframe from "@frontity/components/iframe";

const source = {
  priority: 100,
  name: "source",
  test: ({ node }) =>
    node.component === "p" &&
    node.children[0]?.content?.startsWith("Source:") &&
    node.children[1]?.props?.href?.includes("wikipedia"),
  processor: ({ node, state }) => {
    const linkWikipediaSrc = node.children[1].props.href;
    const { isReady, isPost, id } = state.source.get(state.router.link);
    if (isPost) {
      const tag = node.component;

      const Original = () => React.createElement(tag, null, node.content);

      node.component = () => (
        <>
          <Original />
          <Iframe src={linkWikipediaSrc} height="500" width="100%" />
        </>
      );
    }
    return node;
  },
};

Speaking of headings, the condition used for test: needs to be wrapped like instead:

test: ({ node }) => (
    (node.component === "h2" || node.component === "h3" || node.component === "h4")
    && node.children.length
),

Let me know if this helps.