Skip to content
This repository has been archived by the owner on Jul 28, 2023. It is now read-only.

⚛️ Refactor directives and make them work inside client components #74

Merged

Conversation

luisherranz
Copy link
Member

Fixes #73.

For now, I've just added a simple directive to reproduce the problem. I'll continue working on the solution.

@luisherranz
Copy link
Member Author

I don't like having to traverse all the props again in options.vnode to search for the ones that start with wp-, but I don't see another way around it. Other ideas are welcomed 🙂

@luisherranz
Copy link
Member Author

Well, there are actually options to avoid the double traverse:

  • Marking the static vnodes to bypass the traverse in the options hooks.
  • Attach the attributes to the vnode and move all the traverse to the options hooks.

I think I like the first option better because with the second the static vnodes won't have props until our options hook runs.

@luisherranz
Copy link
Member Author

luisherranz commented Sep 22, 2022

For future reference, a couple of perf tests:

I tested it in Chrome desktop and mobile, Firefox desktop and mobile and Safari desktop.

  • In Chrome, forEach is slightly faster but not for much.
  • In Safari, forEach is quite slow. for and for of are similar.
  • In Firefox, for is faster.

for seems to be the best option in both cases.

@luisherranz
Copy link
Member Author

I think everything is finished here. Ready for review.

https://www.loom.com/share/4f9809e14edc45b49024014a92086862

@luisherranz luisherranz marked this pull request as ready for review September 26, 2022 16:08
@luisherranz
Copy link
Member Author

Oh, something is going on with the block context. It doesn't work after hiding+showing. I'll take a look.

Screen.Capture.on.2022-09-26.at.18-15-39.mp4

@luisherranz luisherranz changed the title Refactor directives and make them work inside client components ⚛️ Refactor directives and make them work inside client components Sep 26, 2022
@luisherranz luisherranz self-assigned this Sep 26, 2022
@DAreRodz
Copy link
Collaborator

DAreRodz commented Sep 26, 2022

Oh, something is going on with the block context. It doesn't work after hiding+showing. I'll take a look.

Hey @luisherranz, I tried to reproduce the issue you mentioned, but after hiding and showing the blocks, the context still works. The blocks were arranged similarly. 😕

I also tested it in main-directives-hydration, just in case, and it also worked.

EDIT: my bad, I was in the wrong commit (9057066). 🤦 In the latest commit, the issue is reproducible.

Copy link
Collaborator

@DAreRodz DAreRodz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@luisherranz, the code seems great. 👍

Regarding the issue, I don't know where it could be TBH. 😕 Maybe it is related to the attributes being processed now inside the wpType directive, or re-processing the wp directives on non-static nodes...

No idea. 🙃

@DAreRodz
Copy link
Collaborator

I've taken another quick look at the block context issue, and I think it should be related to the way sourced attributes are computed after the refactoring (inside the blockType directive).

It is like the title attribute disappears when the node is mounted again.

@luisherranz
Copy link
Member Author

Thanks, David. I'll take a look later today 🙂

@luisherranz
Copy link
Member Author

luisherranz commented Sep 28, 2022

It is like the title attribute disappears when the node is mounted again.

It disappears because it's a sourced attribute and needs to look at the DOM to get the value. At the beginning of the application, we have a DOM and create a vDOM. So we go DOM-> vDOM and the sourced attribute values can be found. But when Preact components add nodes, we go in the other direction, vDOM-> DOM. So there's no DOM to search for the sourced attribute values.

I'm not sure why or how this was working previously. I'll take a look.

@luisherranz
Copy link
Member Author

I'm not sure why or how this was working previously. I'll take a look.

Ok, it makes sense now. You were processing the sourced attributes in the toVdom function, which means they will exist (and persist) in the vnode children (vnode.props.children) that then the component hides/shows using {show && children}. I'm processing them in the options.vnode function, which means they are processed on the fly each time a vnode is created, and therefore it needs to process them again when there's no DOM yet.

I'll think about how we can do this without hardcoding that behavior into the toVdom function.

@luisherranz
Copy link
Member Author

I've finally exposed the vnode and I'm using it a storage medium for the source attributes. Of course I don't love it, but it's the only thing that is stable and at least it's not hardcoded. We can think about better alternatives in the future.

I've also added support for returning components.

This should be ready for review!

https://www.loom.com/share/e71a9867720542eaa5812d70741ae4fd

cc: @DAreRodz

@luisherranz
Copy link
Member Author

luisherranz commented Sep 29, 2022

EDIT: The video is finally loading fine.

Copy link
Collaborator

@DAreRodz DAreRodz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great refactoring, thanks @luisherranz!! 👏

PS: I left a comment below.

Comment on lines 49 to 59
if (!vnode._blockAttributes) {
vnode._blockAttributes = blockAttributes || {};
useMemo(() => {
for (const attr in blockSourcedAttributes) {
vnode._blockAttributes[attr] = matcherFromSource(
blockSourcedAttributes[attr]
)(initialRef);
}
}, [blockAttributes, blockSourcedAttributes]);
}
props.attributes = vnode._blockAttributes;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was intrigued by that useMemo, and I think we may be able to get rid of it.

We are using vnode as a container to store_blockAttributes. That vnode —the original one returned by toVdom()— is not recreated and always exists. So, once you set the _blockAttributes prop, I guess that code won't run again, am I right? 🤔

Apart from that, useMemo is used conditionally, and that's not supposed to be the way. 😅

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're totally right. Fixed.

I've also added a comment to explain why we're doing such a weird thing.

I'm also curious how sourced attributes will play out with the initially hidden blocks (the ones that will use <template> in the future).

@luisherranz
Copy link
Member Author

Thanks, David!! 🎉

@luisherranz luisherranz merged commit cc0982e into main-full-vdom-hydration Sep 29, 2022
@luisherranz luisherranz deleted the fix-directives-inside-client-components branch September 29, 2022 09:45
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

⚛️ Directives don't work if they are used inside of a component
2 participants