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

Creating light components #259

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 141 additions & 0 deletions 0000-light-components.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
- Start Date: (2024-10-6)
- RFC PR: (leave this empty)
- React Issue: (leave this empty)

# Summary

React at first had class components. Creating a class component was hard. So it introduced function components.
Function components were easier to create. They were more readable. They had less boilerplate.
Then server components came into the scene.

Now I would like to propose *light components*. They are simple valid JSX variables or constants. They do not have props and they do not have states. They are just a simple JSX bundle.

# Basic example

```
const Contacts = <div>
<div>some phone number</div>
<div>some email</div>
</div>

<Contacts />
```
Comment on lines +15 to +22
Copy link

@slorber slorber Nov 6, 2024

Choose a reason for hiding this comment

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

Why not just do this instead?

const contacts = (
  <div>
   <div>some phone number</div>
   <div>some email</div>
  </div>
)


const app = (
  <>
    <h1>My App</h1>
    {contacts}
  </>
);

I really don't see the point of introducing this new concept. Instead of light components, you can simply use constant JSX elements.


# Motivation

It's all about unnecessary boilerplate. Our team has built an SPL (software product line). The panel is built using React.
We minimize the boilerplate as much as we can. The reduced boilerplate adds direct business value to us (enhanced maintainability, lower learning curve, faster time-to-market).

One aspect of the boilerplate is to be forced to add at least two parentheses and a lambda (4 characters) to turn a JSX constant into a function component:

```
const Contacts = () => <div>
<div>some phone number</div>
<div>some email</div>
</div>

<Contacts />
```

It does not look much in one single instance. But multiply it by more than 500 lists that we have, and each list has a couple of JSX constants and the value shows itself.

For example, look at this `/SomePath/Product/List.jsx` file:

```
const headers = <>
<th>Name</th>
<th>Price</th>
</>

const row = product => <>
<td>{product.name}</td>
<td>{product.price}</td>
</>

const listActions = <>
<Categories />
<Tags />
<Attributes />
<Export />
</>

const entityActiions = <>
<Images />
<Categories />
<Tags />
<Price />
</>

const Products = <List
title='Products'
entityType='Product'
headers={headers}
row={row}
listActions={listActions}
entityActions={entityActions}
/>

export default Products
```

Rendering a JSX fragment inside a file/component is not hard. You render it like `{jsxFragment}`. But when it comes to big systems where you split the system into parts, and develop part separately and reuse parts, that's when it becomes hard.

# Detailed design

I recommend that we start with a build configuration flag. In CRA (WebPack) or Vite or any other build system we can configure React to treat JSX framgnets/constants as components:

```
{
treatJsxAsComponent: true
}
```

Then we should be able to capitalize the first letter of the JSX constant/variable and render it like a normal component:

```
const LightComponent = <div>Light component without props and state</div>

<LightComponent />
```

The point is that light components won't accept props (they can ignore them with a warning in the console) and they won't have states.

```
<LightComponent someKey="someValue" />
// creates a warning in the console that light components won't support props and those props will be ignored
```

# Drawbacks

Why should we *not* do this? Please consider:

Since we can create this behavior as a progressive configurational behavior, I can think of no drawback. Any team who wants light components can turn on a flag and get a new behavior. Otherwise, React is just like how it was before.

# Alternatives

There are a couple of alternatives. One is to use something like a higher-order component to get the JSX constant, and render it.

```
const Contacts = <div>Some contact data here</div>

const JsxRenderer = ({ jsx }) => {
return <div>
{jsx}
</div>
}

<JsxRenderer jsx={Contacts} />
```
Comment on lines +118 to +128
Copy link

@slorber slorber Nov 6, 2024

Choose a reason for hiding this comment

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

What's the point of JsxRenderer here?

You can just write this and skip this useless boilerplate

const Contacts = <div>Some contact data here</div>

{Contacts}


But that adds to the boilerplate even more. It defies the first goal which is to reduce the boilerplate.

# Adoption strategy

This feature won't be a breaking change. At first, it can be enabled via a flag. That way no team would be affected by it.
It can stay as a flag for the rest of the React lifecycle.
However, if it become a permanent behavior, then no team should be affected. Because both `<LightComponent />` and `{LightComponent}` should be OK and work.

# How we teach this

A new section should be created in the docs called `Light components`. It should explain some limitations for these types of components.
It should also present some examples on when are they useful. And example could be a `<SalesBadge />` simple JSX that can be created in one place and be reused across an online shop without any change.