paramander / contentful-rich-text-vue-renderer

Render Contentful Rich Text field using Vue
https://www.npmjs.com/package/contentful-rich-text-vue-renderer
MIT License
38 stars 12 forks source link

Infinite Update Loop Warning when Adding Newline Break #33

Closed NWRichmond closed 3 years ago

NWRichmond commented 3 years ago

The Problem

According to #29, the recommended way to add <br />s to a content node is to use custom node-renderers as demonstrated /src/index.test.js.

However when using those node-renderers, Vue warns of an infinite update loop:

You may have an infinite update loop in a component render function.

When tested in a Nuxt app, I see the same warning when the dev server is running. When statically generating a page that uses this render function, the app gets stuck in a loop during hydration, and the page fails to load.

Minimal Reproduction

I created a Code Sandbox based on /src/index.test.js - notice the warning:

vue-console-warn

Discussion

It seems like something here needs to change, but I'm not sure what. I'll keep experimenting, but would appreciate any and all help with this. Please feel free to fork the Code Sandbox to experiment.

NWRichmond commented 3 years ago

Closing this after realizing that there's a workaround.

For reasons that aren't clear to me yet, when the document existed as Vue data, there was an update loop when passed to the node-renderers. Returning the document from a method sorted it out (example).

NWRichmond commented 3 years ago

Reopening, since in many real-world cases the document will exist as data, as demonstrated in the README.

tolgap commented 3 years ago

@NWRichmond I figured out what the issue is. In my test example it reassigns node.content, which triggers the component to rerender while rendering the text node. This can be avoided by doing the following:

        [BLOCKS.PARAGRAPH]: (node, key, h, next) => {
          const nodeContentWithNewlineBr = node.content.map(childNode => {
            if (childNode.nodeType === 'text') {
              const splittedValue = childNode.value.split("\n");
              return splittedValue.reduce((aggregate, v, i) => (
                [
                  ...aggregate,
                  { ...childNode, value: v },
                  { nodeType: 'break', key: `${key}-br-${i}` }
                ]
              ), []).slice(0, -1);
            }

            return childNode;
          });

          const content = [].concat.apply([], nodeContentWithNewlineBr);

          return h('p', { key }, next(content, key, h, next));
        }

So we avoid reassigning node.content. See https://codesandbox.io/s/dreamy-bassi-0whgo?file=/src/App.vue

If you want to fix this in the tests and open a PR, I would be very grateful!

NWRichmond commented 3 years ago

Thank you so much, @tolgap! I've opened #34 to make those adjustments to the tests.