kwasniew / hyperbook

https://github.com/kwasniew/hyperbook-tutorial
https://hyperposts.netlify.app/
Other
35 stars 4 forks source link

state provider #5

Open kwasniew opened 4 years ago

kwasniew commented 4 years ago

https://github.com/jorgebucaran/hyperapp/issues/941

kwasniew commented 4 years ago
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Avoiding Prop Driling with Children</title>
    <link rel="stylesheet" href="https://unpkg.com/sakura.css/css/sakura.css" type="text/css">
    <script type="module">
        import {app} from "https://unpkg.com/hyperapp?module";
        import html from "https://unpkg.com/hyperlit?module";

        const state = {user: "matt"};

        const GrandParent = (children) => html`<div>GrandParent ${children}</div>`
        const Parent = (children) => html`<div>Parent ${children}</div>`
        const Child = user => html`<div>${user}</div>`;
        const view = state => GrandParent(Parent(Child(state.user)));

        app({
            init: state,
            view,
            node: document.getElementById("app")
        });
    </script>
</head>
<body>
<main>
    <div id="app"></div>
</main>
</body>
</html>

Inspired by: https://www.youtube.com/watch?time_continue=941&v=3XaXKiXtNjw&feature=emb_logo

krzychukula commented 4 years ago

I would say the opposite. Using children to avoid prop drilling looks to me like it has really specific situations when it can be used. Even in the video he still says that context is better for truly global data like the theme, user preferences (and probably user as it's a silly example).

The biggest disadvantage is that you lose abstraction. Now imagine GrandParent is used 50 times in your codebase. The Parent is even more applicable and you find 200+ matches. In my experience when you get into a bigger project - especially if it's actively worked on by a bigger team the only practical solution is to use some kind of context.

Of course, bigger refactoring would get you the same thing. But, if we're talking about getting the same thing then it just doesn't make it worth it.

Something I'm not sure about is when does it stop? When I take this example further it seems like my whole view would be a super complex and super long file with really deeply nested views to avoid prop drilling. It doesn't sound maintainable to me.

I was thinking about proposing some rule of thumb rules like:

  1. Use prop drilling for shallow stacks
  2. Use the context for global data

I'm struggling to figure out when I would recommend render props (or in this case render children pattern—I can't see a difference).

More real example?

I'm sorry but I struggle to think through code examples that are abstract. Wouldn't you mind using more real-life views?

        import {app} from "https://unpkg.com/hyperapp?module";
        import html from "https://unpkg.com/hyperlit?module";

        const state = {theme: "ligth"};

        const Root = (children) => html`<div>App ${children}</div>`
        const Header = (children) => html`<div>Header ${children}</div>`
        const Logo = theme => html`<div>${theme}</div>`;
        const view = state => Root(Header(Logo(state.user)));

        app({
            init: state,
            view,
            node: document.getElementById("app")
        });

The benefit of my examples (for me) is that it makes it more obvious for me that I would struggle to work with this pattern probably beyond maybe 20 lines of view code. It's a guess on my side, but I'm pretty sure there would be a limit to the practice.

What I'm thinking is that we need to have a couple of solutions.

Something for shallow drilling within a small scope.

  1. Just pass the props
  2. Try the render view pattern explained above

Something for truly global data like theme

  1. Use context

Something for code in between: It's not truly global, but it's not in only one place.

kwasniew commented 4 years ago

I need to think about it. But the moment you start using context you loose local reasoning capability. You always have some state at a distance. This is a problem the Reader monad solves so maybe some inspiration from there.