goober icon indicating copy to clipboard operation
goober copied to clipboard

Cannot read property 'data' of undefined

Open joneslloyd opened this issue 3 years ago • 10 comments

I'm using Goober with Preact (and Twin), and I'm having some difficulties making Goober play ball with Shadow DOM.

When I try to run something like this:

const shadowRoot = document.getElementsByTagName('app-shadow-section')[0] || false;
if( shadowRoot ) {
  goober.css.call({ target: shadowRoot }, `color: red !important;`);`
}

...it does correctly add the styles (color: red !important;) to the shadow component (app-shadow-section), but it also gives a console error that kills the app:

Uncaught (in promise) TypeError: Cannot read property 'data' of undefined
    at goober.modern.js:1
    at s (goober.modern.js:1)
    at Object.p (goober.modern.js:1)
    at a (goober.modern.js:1)
    at p.constructor (goober-global.modern.js:1)
    at p.M [as render] (preact.module.js:1)
    at I (preact.module.js:1)
    at m (preact.module.js:1)
    at I (preact.module.js:1)
    at m (preact.module.js:1)

(As an aside, if I try to get the styles from a shadow component (const styleTag = ``<style id="_goober">${goober.extractCss( shadowRoot )}</style>``;) I get a similar error:

goober.modern.js:1 Uncaught (in promise) TypeError: Cannot read property 'data' of undefined

In both cases, the shadow component exists in the DOM (as I can console log its dom node, and see the style correctly applied to it). I thought it might be related to lifecycle (etc) so tried the same code in useEffect, but the results are exactly the same.

joneslloyd avatar Apr 19 '21 14:04 joneslloyd

Similarly, if I try to get around the issue by defining a styled component and then pulling its styles from <head> and then directly applying them to my shadow dom component, I get the same error (even though those styles are applied to the component) :

(It's using some Twin syntax; feel free to ignore, given the issue seems to pertain to Shadow DOM, and the following code works if the target node is a 'light' DOM node, ie, not a shadow DOM node).

const target = //MY SHADOW COMPONENT;
const AppShadowSectionStyles = goober.styled('app-shadow-section')(() => [
        tw`h-full w-full min-h-full max-w-screen-xl`,
    ]);
const s = goober.extractCss();
goober.css.call({ target }, s);

This 'works' (in the sense that the styles are correctly applied to my shadow component), but the error kills the app.

joneslloyd avatar Apr 19 '21 14:04 joneslloyd

Hey @joneslloyd! Thanks for opening this issue!

This is most certainly coming from the fact that there isn't a textNode inside the style tag that you are trying to target; hence the missing .data property on it. So with this, that should be:

const styleTag = `<style id="_goober"> ${goober.extractCss( shadowRoot )}</style>`
                                   // ^--- notice the empty string

cristianbote avatar Apr 19 '21 15:04 cristianbote

Hey @cristianbote ! Thanks for the super-fast response!

Doing as you've suggested still doesn't seem to do it unfortunately :(

Screenshot 2021-04-19 at 16 02 52

Also do you have any suggestions as to why it is happening with goober.css.call({ target: shadowRoot }, ``color: red !important;``); ?

(Ignore the double backticks here; it's just so that GitHub displays it more readably).

joneslloyd avatar Apr 19 '21 15:04 joneslloyd

Sorry I should add: The main thing I'm trying to do is change the targets so that the CSS is put into a <style> tag on my shadow component, but when I do this it's ignored:

const css = goober.css.bind({ target: target });
const styled = goober.styled.bind({ target: target });

joneslloyd avatar Apr 19 '21 15:04 joneslloyd

Do you happen to have a handy codesandbox at hand? I'm struggling to reproduce it. I revived this web-components demo https://codesandbox.io/s/immutable-moon-x7k7xwz6mz just to make sure everything works, and it seems so. So, any chance you can reproduce it in codesandbox?

cristianbote avatar Apr 19 '21 19:04 cristianbote

Hey – I have the public repo itself if that helps?

But I can put it onto CodeSandbox if that's easier?

(Note: In this repo I've hacked together a kind-of workaround in /src/widget/index.js by manually moving the CSS from the <head> to inside the Shadow DOM, via pure JavasScript).

In an earlier version, inside the same useEffect function, I set goober.css.bind and goober.styled.bind, which weren't working for me.

joneslloyd avatar Apr 19 '21 19:04 joneslloyd

Oh ok, tried to run the repo, but I bet I am missing some env variables that would make it load. I've tried to take out as much but still can't reproduce the above issue. Even removed the entire useEffect. Any chance I can isolate the functionality more than that?

cristianbote avatar Apr 19 '21 19:04 cristianbote

Oops, sorry about that. The ENV vars aren't secret or anything; rather, there were different ones for local development.

Try creating a .env file in the project root with:

DATA_API=https://widget.mobalytics.gg/lol/graphql/v1/query
SQUIDEX_API=https://widget.mobalytics.gg/league/gql/static/v1
CDN_URL=https://cdn.mobalytics.gg

I will also try to get a codesandbox together later

joneslloyd avatar Apr 19 '21 19:04 joneslloyd

Sorry for the slow(er) response.

~~Here's the CodeSandbox: https://codesandbox.io/s/builds-widget-v4sld~~

~~In /src/widget/index.js you can see my hack 'solution'. In this same area, I was previously trying to change the target via:~~ ~~- goober.css.bind({ target: target })~~ ~~- goober.styled.bind({ target: target })~~

UPDATE:

This is at least partially my fault for incorrectly implementing a Shadow DOM component. Sorry about that.

However, there is unexpected (incorrect?) behaviour when using extractCss.

Here is a newer CodeSandbox. If you check out /src/widget/app/components/app-shadow-section/index.js, you'll see I am now using css.call and extractCss without any console errors.

The issue though is that only the classes of the component that the code is used in are extracted (rather than all of them for the entire app, which is the desired behaviour). Is this a bug, or just an error in my code/approach/architecture?

FURTHER UPDATE:

Changing this:

const appShadowSectionRef = useRef(null);

  useEffect(() => {
    const currentShadowSection = appShadowSectionRef.current;

    if (currentShadowSection) {
      goober.css.call(
        { target: currentShadowSection.base },
        goober.extractCss()
      );
    }
  }, [appShadowSectionRef]);

To this:

    const appShadowSectionRef = useRef(null);
    const [appliedStyles, setAppliedStyles] = useState(false);

    useEffect(() => {
        const currentShadowSection = appShadowSectionRef.current;

        if (!appliedStyles && currentShadowSection) {
            goober.css.call({ target: currentShadowSection.base }, goober.extractCss());
            setAppliedStyles(true);
        }
    }, [appShadowSectionRef]);

Results in all of the styles being applied to the new Shadow DOM <style> tag, but it appears they are one version behind, if that makes sense?

By that I mean that it appears to contain all of the styling, which is great, but the auto-generated class names (.go490062198 .go2586802571) don't match with any of the classes used. So I assume this is from some previous state of the classes (or something else is going on).

I realise that this has pivoted way beyond the original ticket! If it's something you don't want to address, I totally understand 👍

joneslloyd avatar Apr 20 '21 09:04 joneslloyd

~~I'm having the same problem with Svelte Kit, I'm trying to use Svelte Kit Hooks to add content to the <style id="_goober"> element in the server, then in the client, running extractCss with document.getElementById('_goober').~~

~~Even seems like Goober can not accept an already filled style tag.~~

My problem was solved by defining window._goober like this:

window._goober = document.getElementById('_goober')
extractCss(window._goober)

Actually running extractCss does not even seem to be needed if you don't want hydration

phiberber avatar Jun 17 '22 14:06 phiberber