Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Nextjs] cssinjs breaks by streaming mode #45955

Closed
infodusha opened this issue Nov 18, 2023 · 4 comments · Fixed by #46500
Closed

[Nextjs] cssinjs breaks by streaming mode #45955

infodusha opened this issue Nov 18, 2023 · 4 comments · Fixed by #46500

Comments

@infodusha
Copy link

infodusha commented Nov 18, 2023

Reproduction link

https://github.com/infodusha/antd-next-streaming-issue

Steps to reproduce

There are actually two bugs with different reasons:

  1. Have a streamed page with timeout, that you navigate from different page and it has any elements that have prepended css.
  2. Have a streamed page with timeout, that you directly navigate, and you already rendered some styles before the streaming. You will see flicker.

What is expected?

Both bugs are expected not to happen:

  1. prepended css supposed to be injected on navigation.
  2. streamed content also supposed to render styles on the server.

What is actually happening?

  1. Prepended css is injected with wrong specificity with leads to color loose.
  2. For streaming content it is being falsy deduplicated.
Environment Info
antd 5.11.2
React 18.2.0
System Mac OS
Browser Chrome
@infodusha
Copy link
Author

Screen.Recording.2023-11-18.at.21.09.32.mov

Here is quick demo of both issues

@infodusha
Copy link
Author

Also i've found kinda a workaround for both bugs:

'use client';

import { useRef, useState } from 'react';
import { useServerInsertedHTML } from 'next/navigation';
import { createCache, extractStyle, StyleProvider } from '@ant-design/cssinjs';

export function AntdRegistry({ children }: React.PropsWithChildren) {
  const [cache] = useState(() => createCache());

  const isServerInserted = useRef(false);

  useServerInsertedHTML(() => {
    if (isServerInserted.current) {
      // We would like to avoid duplicated css insertion
      // But it seems to break the style when streaming
      // return;
    }
    isServerInserted.current = true;
    const attributes = htmlToAttributes(extractStyle(cache));
    return <style {...attributes} />;
  });

  return (
    <StyleProvider cache={cache} hashPriority='high'>
      {children}
    </StyleProvider>
  );
}

const startTagRegex = /^<(.+?)>/;

function htmlToAttributes(html: string) {
  const rootTagContentMatch = startTagRegex.exec(html);

  if (!rootTagContentMatch) {
    throw new Error('htmlEjectAttributes: invalid html');
  }
  const [fullMatch, content] = rootTagContentMatch;
  const htmlAttrsRegex = /\s(\w+)="(.+?)"/g;

  let match;
  const argPairs = [];
  while ((match = htmlAttrsRegex.exec(content))) {
    argPairs.push([match[1], match[2]]);
  }
  const [rootTag] = content.split(' ');

  const __html = html.slice(fullMatch.length, html.lastIndexOf(`</${rootTag}>`));
  return argPairs.reduce((acc, [name, value]) => ({ ...acc, [name]: value }), {
    dangerouslySetInnerHTML: { __html },
  });
}

It seems to me that the first bug is cause by the fact that extractStyle is called with plain: true, that leads for style tag attributes being lost.
The second bug is caused by the dedupe logic, since when streaming useServerInsertedHTML is called for the child we need to send extra tags if that child rendered anything.

My solution works in general, but I wish to have an api to get attributes from extractStyle to solve first bug and some different dedupe logic (it supposed to check, what have already sent and what styles in cache are new - currently streaming content includes pre-stream styles)

@MadCcc
Copy link
Member

MadCcc commented Nov 22, 2023

Seems same issue as #45894

@infodusha
Copy link
Author

Indeed, partly. The first bug is caused by a different reason.

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

Successfully merging a pull request may close this issue.

2 participants