VulcanJS / vulcan-npm

The full-stack JavaScript App Framework
https://vulcan-docs.vercel.app
MIT License
31 stars 8 forks source link

RFC: Magical replacement of components #17

Open eric-burel opened 4 years ago

eric-burel commented 4 years ago

Cross post of https://github.com/VulcanJS/Vulcan/issues/2549

Dream goal

Goal is to replace the dynamic Component replacing pattern by explicit exports that would "magically" replace components.

Imagine a component structured like this:

// datatable.js
const DatatableHeader = () => (<header>default header</header>)

const DatatableFooter = () => (<footer>default footer</footerr>)

const DatatableContent = ({children}) => (
    <div>
        <DatableaHeader />
            {children}
        <DatatableFooter />
</div>

const Datatable = () => (
    <div>
        <DatatableContent />
    </div>
)

You may want to replace the Header, and the Content with your own components:

// my-overrides.js, a special file that contains my Vulcan components override. Could be detected at build time.
export * from "vulcan/ui-react" // magically imports DatatableFooter, DatatableHeader, DatatableContent and Datatable
export const DatatableHeader = () => (<header> I AM OVERRIDEN </header>) // first override
export const DatatableContent = () => (<div><DatatableHeader/> <div> Hello </div> <DatatableFooter /></div> // second override

Then you consume them like this, business as usual:

// my-page.js
import { Datatable } from "vulcan/ui-react" // import default components + my custom override

export const MyPage = () => (<Datatable>Hello!</Datatable>) // DatatableContent has magically been replaced in Datatable

It's probably undoable with Meteor, but it could prove doable with Webpack. The tricky part is that you want to override only certain component, that are themselves nested into other replaceable component. You get a kind of recursive/circular import issue.

What's doable today

This pattern is already implementable with one level of imports like this:

// my-ui.js
export * from "vulcan/react"
export const DatatableHeader = () => (...) // will reexport, overriding default DatatableHeader from "vulcan/react"

but it's not enough to handle more deeply nested component replacement, for complex components like DataTable or SmartForm that have multiple layers of replaceable component.

Possible implementation

Step 1: detecting "magic" replacement

First step is defining a syntax for magic components.

Possibilities:

Step 2: build

The difficulty is to handle deeply nested replaceable component.

The build would probably look like a recursive structure or a tree, where we rebuild everything until all nested components are replaced by user's overrides.

EloyID commented 4 years ago

And sth like export const SmartForm = customizeSmartForm({components: {FormHeader: MyOwnFormHeader}}) in your own packages? We can reuse the components prop to override those used for the smartForm like a HOC

customizeSmartForm = (props) => { return (props2) => <SmartForm {...props} {...props2}> }

eric-burel commented 4 years ago

That's a way indeed, you would define your component as a closure: const SmartForm = components => (props) => {...} so you can pass different type of components

Currently the Datatable accepts something similar but with components as a prop, so technically we could already be able to implement something like that.