Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds support for root component #1009

Merged
merged 10 commits into from
Feb 20, 2023
Merged

Adds support for root component #1009

merged 10 commits into from
Feb 20, 2023

Conversation

infomiho
Copy link
Contributor

Root component

Fixes #77

This PR introduces rootComponent in the app.client dictionary in Wasp lang which will help users to define their root React component.

rootComponent is of type ExtImport which is an import statement from user defined files.

This enables the user to do:

  1. Define a src/client/App.jsx which exports a component
  2. Use the component to setup various providers e.g. Redux or ChakraProvider https://chakra-ui.com/getting-started

We then use their root component to wrap the router in the app's index.js.

@infomiho infomiho requested review from sodic and Martinsos and removed request for sodic February 16, 2023 08:41
Comment on lines 40 to 45
{=# rootComponent.isDefined =}
<{= rootComponent.importIdentifier =}>{router}</{= rootComponent.importIdentifier =}>
{=/ rootComponent.isDefined =}
{=^ rootComponent.isDefined =}
{router}
{=/ rootComponent.isDefined =}
Copy link
Member

Choose a reason for hiding this comment

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

Nitpick: What about:

Suggested change
{=# rootComponent.isDefined =}
<{= rootComponent.importIdentifier =}>{router}</{= rootComponent.importIdentifier =}>
{=/ rootComponent.isDefined =}
{=^ rootComponent.isDefined =}
{router}
{=/ rootComponent.isDefined =}
{=# rootComponent.isDefined =}
<{= rootComponent.importIdentifier =}>
{=/ rootComponent.isDefined =}
{router}
{=# rootComponent.isDefined =}
</{= rootComponent.importIdentifier =}>
{=/ rootComponent.isDefined =}

is this maybe nicer / more composable?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It will be easier to add new user defined wrappers, but will we have the need to add them in the future?

Your approach has extra whitespace in the output when not using rootComponent e.g. {router} 😂

Copy link
Member

Choose a reason for hiding this comment

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

Yeah I know but I gave up on formatting, we can't do it 100% anyway.

I think what I wrote is more explicit about "we are just wrapping it into this".

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Makes sense, I'll probably need to give up on output formatting as well 😢

Maybe in the future, one of our build steps can be running everything through Prettier auto-format and then the users will get super nice output :D

Copy link
Member

Choose a reason for hiding this comment

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

yes that is probably what we will want to do! Or we will give up on it completely. But I like the idea of prettier.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated here: 207806c

@Martinsos
Copy link
Member

Nice job -> your first extension of Wasp language itself :D!

Oh one thing is missing though -> docs, right? Let's add them in this PR, no reason to do them in separate PR.

@Martinsos Martinsos self-requested a review February 16, 2023 09:35
Copy link
Member

@Martinsos Martinsos left a comment

Choose a reason for hiding this comment

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

Let's add docs?

@infomiho
Copy link
Contributor Author

I've updated the docs in the meantime, check the last commit 🙂

Copy link
Member

@Martinsos Martinsos left a comment

Choose a reason for hiding this comment

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

LGTM! Some small comments, resolve as you find fit and you can merge!

Comment on lines 1425 to 1440
export default async function Root({ children }) {
return (
<Provider store={store}>
<App>{children}</App>
</Provider>
)
}

function App({ children }) {
return (
<div>
<h1>My App</h1>
{children}
</div>
)
}
Copy link
Member

Choose a reason for hiding this comment

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

WHy do we need this extra App here? Can't we just have Root render children directly in Provider component and that is it?

Copy link
Contributor Author

@infomiho infomiho Feb 16, 2023

Choose a reason for hiding this comment

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

I wanted to offer a more elaborate example where we have the Provider wrapping and some <App /> compnent (which is how you'll see it's done in the Redux docs).

This also works:

export default async function Root({ children }) {
  return (
    <Provider store={store}>
      {children}
    </Provider>
  )
}

Copy link
Member

Choose a reason for hiding this comment

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

Ok! It is good to give exapmles, but I sometimes have the need to reduce them if possible, to make maintainance simpler. But this is probably ok it is not so complex. Unless this might confused them -> do I have to use this App inside this thing? I don't know, your call!

Copy link
Contributor Author

@infomiho infomiho Feb 16, 2023

Choose a reason for hiding this comment

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

I'll provide two use-case examples (Layout + Providers) and a one more complex where both are coming together.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated here: 207806c

waspc/src/Wasp/Generator/WebAppGenerator.hs Outdated Show resolved Hide resolved
waspc/src/Wasp/Generator/WebAppGenerator.hs Outdated Show resolved Hide resolved
web/docs/language/features.md Outdated Show resolved Hide resolved
Comment on lines 258 to 259
mkTmplDataForExtImport :: Maybe ExtImport -> Aeson.Value
mkTmplDataForExtImport maybeExtImport = maybe notDefinedValue mkTmplData maybeExtImport
Copy link
Contributor

Choose a reason for hiding this comment

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

This function encapsulates a common pattern.

If I understand correctly, we're using it for all ext imports. Do you think it makes sense to put it in a more public place and reuse?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This can be extracted to a common function in Wasp.Generator.JsImport and then it can be specialized in Wasp.Generator.WebAppGenerator.JsImport and Wasp.Generator.ServerGenerator.JsImport.

In the web app templates, this pattern is actually only used in this file, but in the server templates it's used more.

I'll extract the pattern in this PR and apply it to WebApp only. I'll do the same for server templates in a separate PR.

Copy link
Contributor Author

@infomiho infomiho Feb 16, 2023

Choose a reason for hiding this comment

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

We now have:

  • Wasp.Generator.JsImport -> jsImportToTmplData which generates appropriate JSON for usage in templates when we need to do something with import statements
  • Wasp.Generator.WebAppGenerator.JsImport -> extImportToJsImportTmplData which uses the above method to convert an ExtImport to JSON template data

We should add in a separate PR:

  • Wasp.Generator.ServerGenerator.JsImport -> extImportToJsImportTmplData
  • use the new function to refactor the way we generate template data for JS import statements

Copy link
Contributor

@sodic sodic Feb 17, 2023

Choose a reason for hiding this comment

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

To keep in line with naming conventions for functions (i.e., they should be verbs) and what we already have (like this guy here*), I suggest renaming this to something like makeImportJson.

The "functions should be verbs" applies to other places as well.

* Although that name could have been better as well :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We have a number of these "tranform" functions in our codebase, and I kinda like this convetion of <A>To<B> naming.

Some examples I found in our codebase:

  • kebabToCamelCase
  • sourceSpanToRegion
  • lspPositionToOffset
  • extImportToJsImport (that's mine)

What about extImportToImportJson?

Copy link
Contributor

Choose a reason for hiding this comment

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

That's true, we have several of those.

Anyway, choose whatever you like best.

Purely FYI, this is my order of preference:

  1. makeExtImportJson
  2. extImportToImportJson
  3. extImportToJsImport

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm happy to inform you that I went with your second favourite option 🎉

@infomiho infomiho merged commit c2a3e04 into main Feb 20, 2023
@infomiho infomiho deleted the miho-root-component-feature branch February 20, 2023 10:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Enable custom wrapping of top-lvl React component
3 participants