<p dir="auto">This came up in a <a href="https://twitter.com/devongovett/status/1199862177559171072?s=21" rel="nofollow">twitter thread</a> with <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/gaearon/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/gaearon">@gaearon</a> and <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/aweary/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/aweary">@aweary</a> and we decided to move the discussion here. I’ll try to summarize the conversation so far below.</p>
<p dir="auto">The basic question was whether a build tool could extract the static parts of a component tree ahead of time into HTML, and ship smaller JS to the client with only the dynamic parts. This could have benefits for code size and hydration performance for statically generated sites. It would likely have a smaller impact on dynamically generated server rendered sites, but it’s possible there are static parts that could be extracted there too (e.g. header, footer, article content, etc.).</p>
<p dir="auto">There are a couple potential ways to go about this, each with various tradeoffs.</p>
<ol dir="auto">
<li>Do something like what ember and other template compilers do and generate some kind of IR from components and inject dynamic content into slots at runtime. This would likely require a lot of changes to React itself. <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/aweary/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/aweary">@aweary</a> seems to be working on some kind of <a href="https://twitter.com/aweary/status/1199867044532903937?s=21" rel="nofollow">compiler</a> to do just that.</li>
<li>Rewrite the JS with something like prepack, similar to what <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/trueadm/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/trueadm">@trueadm</a> did <a href="https://github.com/trueadm/ssr-with-prepack-hackathon">here</a> (though it sounded like the output was quite variable in size).</li>
<li>Rewrite the component tree to generate a different but equivalent tree with the static parts hoisted out. Similar to this <a href="https://github.com/jamiebuilds/babel-react-optimize">babel plugin</a> but taken much farther to work at a whole tree level instead of a component level. Also to remove the static parts from JS altogether and generate static HTML to avoid duplicate content in JS and unnecessary hydration cost. Some way to allow static HTML in the middle of a tree to be reused might be needed, but maybe compiling to multiple roots would work?</li>
</ol>
<p dir="auto">Obviously a lot more thought is needed here. As <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/gaearon/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/gaearon">@gaearon</a> noted, a solid definition of “static” will be important for this discussion. Mine is that it could be rendered to HTML and never updated by JS, but perhaps people have other ideas.</p>
<p dir="auto">The problem is best expressed in terms of an example:</p>
<p dir="auto">I've a function that acts as an plugs into my implementation of an isomorphic style loader. The function is simply set on the very high-level <code class="notranslate">AppContext</code> (that contains the application services, and components and the whole shebang) which is passed through root component as a <code class="notranslate">context</code>. So, a component inside essentially just needs to get that style applier function from the context and run them. And naturally, abstracting it into a higher order component would make the most sense to initiate the styles and dispose of them, paired with the lifecycle.</p>
<p dir="auto"><strong>Note:</strong> A similar live implementation running my site, <a href="https://www.prasannavl.com" rel="nofollow">https://www.prasannavl.com</a> is here: <a href="https://github.com/prasannavl/prasannavl.com/blob/0060cc4acbd8796f2495a68c3669b1b224476b15/src/modules/core/createStyled.ts">https://github.com/prasannavl/prasannavl.com/blob/0060cc4acbd8796f2495a68c3669b1b224476b15/src/modules/core/createStyled.ts</a> (However, since its just my site, I'm duplicating the class to workaround the below mentioned problem)</p>
<p dir="auto">However, I <strong>DO NOT</strong> want to couple this component into my app's context. So, essentially, I want something like this:</p>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="export function createStyledWith&lt;T&gt;(styleApplier: CssStyle.StyleApplierFunction, InnerComponent: T, ...styles: any[]) {
    class StyledComponent extends React.Component&lt;any, any&gt; {
        static displayName = getDisplayName(InnerComponent);
        private removeCss: () =&gt; void;

        componentWillMount() {
            this.removeCss = styleApplier.apply(this, styles);
        }

        componentWillUnmount() {
            const removeCss = this.removeCss;
            setTimeout(removeCss, 0);
        }

        render() {
            return React.createElement(InnerComponent as any, this.props);
        }
    }
    // Trick the type system into thinking its the same component
    return StyledComponent as any as T;
}"><pre class="notranslate"><span class="pl-k">export</span> <span class="pl-k">function</span> <span class="pl-en">createStyledWith</span><span class="pl-c1">&lt;</span><span class="pl-smi">T</span><span class="pl-c1">&gt;</span><span class="pl-kos">(</span><span class="pl-s1">styleApplier</span>: <span class="pl-smi">CssStyle</span><span class="pl-kos">.</span><span class="pl-smi">StyleApplierFunction</span><span class="pl-kos">,</span> <span class="pl-smi">InnerComponent</span>: <span class="pl-smi">T</span><span class="pl-kos">,</span> ...<span class="pl-s1">styles</span>: <span class="pl-smi">any</span><span class="pl-kos">[</span><span class="pl-kos">]</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
    <span class="pl-k">class</span> <span class="pl-smi">StyledComponent</span> <span class="pl-k">extends</span> <span class="pl-smi">React</span><span class="pl-kos">.</span><span class="pl-c1">Component</span><span class="pl-kos">&lt;</span><span class="pl-smi">any</span><span class="pl-kos">,</span> <span class="pl-smi">any</span><span class="pl-kos">&gt;</span> <span class="pl-kos">{</span>
        <span class="pl-k">static</span> <span class="pl-c1">displayName</span> <span class="pl-c1">=</span> <span class="pl-en">getDisplayName</span><span class="pl-kos">(</span><span class="pl-smi">InnerComponent</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
        <span class="pl-k">private</span> <span class="pl-c1">removeCss</span>: <span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-smi"><span class="pl-k">void</span></span><span class="pl-kos">;</span>

        <span class="pl-en">componentWillMount</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
            <span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">removeCss</span> <span class="pl-c1">=</span> <span class="pl-s1">styleApplier</span><span class="pl-kos">.</span><span class="pl-en">apply</span><span class="pl-kos">(</span><span class="pl-smi">this</span><span class="pl-kos">,</span> <span class="pl-s1">styles</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
        <span class="pl-kos">}</span>

        <span class="pl-en">componentWillUnmount</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
            <span class="pl-k">const</span> <span class="pl-s1">removeCss</span> <span class="pl-c1">=</span> <span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">removeCss</span><span class="pl-kos">;</span>
            <span class="pl-en">setTimeout</span><span class="pl-kos">(</span><span class="pl-s1">removeCss</span><span class="pl-kos">,</span> <span class="pl-c1">0</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
        <span class="pl-kos">}</span>

        <span class="pl-en">render</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
            <span class="pl-k">return</span> <span class="pl-smi">React</span><span class="pl-kos">.</span><span class="pl-en">createElement</span><span class="pl-kos">(</span><span class="pl-smi">InnerComponent</span> <span class="pl-k">as</span> <span class="pl-smi">any</span><span class="pl-kos">,</span> <span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">props</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
        <span class="pl-kos">}</span>
    <span class="pl-kos">}</span>
    <span class="pl-c">// Trick the type system into thinking its the same component</span>
    <span class="pl-k">return</span> <span class="pl-smi">StyledComponent</span> <span class="pl-k">as</span> <span class="pl-smi">any</span> <span class="pl-k">as</span> <span class="pl-smi">T</span><span class="pl-kos">;</span>
<span class="pl-kos">}</span></pre></div>
<p dir="auto">The problem: How do I create a <code class="notranslate">styleApplier</code> that pull the service from the context?</p>
<p dir="auto">I simply cannot pass the decoupled style applier function into this component, unless I have it stored else where.</p>
<p dir="auto"><strong>Why?</strong></p>
<p dir="auto">I have no access to the context outside of the component. Well, that can be solved by providing a closure that accesses the context, and pull the services. (Or so I thought). But since this doesn't have <code class="notranslate">contextTypes</code>, React will refuse to acknowledge the existence of context. So, there's really no way to make this work without another un-necessary abstraction - another higher order component - that pulls the styleapplier out of the context, and then provides it to this component that can then be called.</p>
<p dir="auto">Higher order components may be gold. But if you fill an entire ship with gold, it will sink. Its great that the React community is insisting on the HOC pattern. But it seems a little dogmatic for that to be the only way to do certain things. Because, its seems totally wasteful to do this for components that are tiny, and exists in hundreds. HOCs may only have small processor time requirement, but does consume memory with each component. More diffing for react, more things to keep around, more lifecycle method executions and so on.</p>
<p dir="auto">Note that, I'm only using the above as an example. My concern is more of the pattern involved. I encounter this on many scenarios, and seem to have to jump through hoops to access these if I do not want to use more higher order components, with no other good way to solve this.</p>
<p dir="auto">Things that <em>could</em> potentially solve this:</p>
<ul dir="auto">
<li>Most reasonable one IMO: Make context validation optional. Don't shove it down the component's throat.</li>
<li>Provide a way to access context from the outside without using higher order components. (Well, I think this kinda breaks many encapsulations, but just leaving it out there since it would theoretically solve the issue)</li>
</ul>