A global CLI for running a single component
gaearon opened this issue · 24 comments
This is something @lacker was working on before Create React App. That project got shelved but I think we could revive it in some form now.
The idea is that instead of creating a project, you just start with a single file that is the root component:
App.js
import React from 'react';
export default function App() {
return <h1>Hello</h1>;
}
run-react-component App.js
It would use the same infrastructure as Create React App (exact same Babel and Webpack settings and CLI output), but would work in any folder without npm start
.
So you could run-react-component NavBar.js
and see just that component, regardless of where it is in the project, and whether it is in a project or not. The only run-react-component
requirement is that whatever you import
(e.g. react
) exists in node_modules
.
Benefits of this approach:
- Can jump right into prototyping with a single file. (#642)
- The install cost is only paid once, you can run
run-react-component
right away in any folder on the disk. (The downside is you actually have to update global CLI to get updates.) - We could offer a way to “convert” such a prototype into a full CRA-based project, and they will be compatible.
- You could even use that in a CRA-based project to work on a single component at a time.
What do you think? Would that be useful?
@gaearon I liked that. I guess it wouldn't work for the case where a component looks at a URL and figues something based on that, correct? Or, maybe it would be possible to pass the location prop through the CLI as well?
What about the props passed to the component ? Where would they be defined ? I think that's what would make the tool worth to use or not. Ideally a UI to tweak the props in realtime :-)
The styling of the component is also important. Usually I import a .less file but sometimes it just overrides styles defined at the top of the project. Without the right style, the component isn't displayed correctly and is unusable.
How would that work with this tool ?
@tugberkugurlu you could have a defaultProp in place of a url that's passed in?
@dperetti defaultProps should get you most of the way there, you could also read a NODE_ENV/command line value for specific stuff. Further, you should really consider writing styles isolated to the component; depending on the cascade of classnames etc wouldn't make the component standalone in any case. That said, it's usually not as bad you'd imagine!
Good idea! Besides all the benefits @gaearon said, it could also be very useful to beginners in react. They could go right into creating a new component and understand quickly how react works.
Would it work with component that have children components too? Do children have to be in the same folder as the root component?
import React from 'react';
import ChildrenComponent from 'components/childrenComponent'
export default function App() {
return (
<div>
<h1>Hello</h1>
<ChildrenComponent />
</div>
)
}
Child components and CSS would be importable relatively, just like they are in Create React App.
Great initiative !
Agreed with @dperetti. As this CLI will be useful for beginners/prototyping, the way props are handled is really important. Reactive UI is a good idea, to actually see the component change realtime. We can also imagine directly passing props from the CLI like:
run-react-component App.js --prop1=foo --prop2=bar
Not sure why there's need to specify props as CLI when you can do this in JS! Simpler and doesn't require restarting when props change.
@dperetti It's not too much extra work to support exporting a React element once you have functionality for supporting exported components in place.
@andreypopp contributed this exact functionality to react-heatpack and it worked nicely.
@insin with functional components this is probably not needed anymore:
export default () => <MyComponent prop1={42} />
@andreypopp @insin That would mean having a webpack dev server running? Or is it possible without? It's a little big heavy to "just" prototype a single component no?
You can specify props with defaultProps
on the component itself, or with a wrapping component.
For example you can create Button.example.js
that just renders <Button />
with a few different colors and prop combinations. Then use run-react-component Button.example.js
to quickly serve just that page and quickly iterate on it. You could even run it side by side with your main development server.
Not sure what you mean by "heavy". Yes it would run the same development server as CRA itself under the hood.
Do we really need another project for that? Maybe it is better to rename CRA to react-cli
and make it host both CRA functionality and proposed run-react-component
?
react create-app
react run-component
@gaearon Got it. I didn't understand it was CRA under the hood the first time you answered me, my bad :)
@andreypopp renaming CRA is not a good idea to me since it is pretty recent. That would confuse people :/
I'm not a big fan of forcing that defaultProps
must implement all props. It changes a bit the semantic of defaultProps
to me. Some components (most of them actually) have props that can't be defined trivially. Any component that takes a model data. Or things like Relay component (but this is another topic, could imagine having run-react-relay-component :D)
Maybe another way would be to have a convention like you have to define a Comp.mock.js
next to Comp.js
in the case Comp
is not renderable with no props? e.g. that Comp.mock.js:
export default () => <Comp {...someProps} />
.
Pushing initial idea a bit, I think if you can render a component standalone like this, another nice property is you could also jest-snapshot-test it automatically! We could have the jest snapshot feature for free, config-less, without ever have written a single line of test.
I think this is really tool where it helps to onboard a lot of new users to React.
Also this will be a good tool for when developing your components.
I think this is a smaller version of React Storybook
By 'run a component' - do you mean - invoke its render method once, and print what's returned?
If printing, does it look like reactElements or JSX ? I think I need some sample output to picture it.
This is a great idea! I had something similar in a project I was working on, but it was implemented as a separate webpack build that rendered the desired component into a full screen container (we were aiming to create a fully responsive experience). I found that this was great not just for isolating components, but it was actually really helpful for styling shared components because it helped enforce keeping layout/positioning styles where they belonged. Doing this with a CLI is a much nicer experience.
As @arunoda mentioned, this seems similar in nature to React Storybook and its ClojureScript predecessor, devcards, so it would probably be good to examine libraries like these to see the current state of the art and gain insight they may have learned along the way.
The most interesting problem I see right now is determining how to pass props (or even context) to a component. Off the top of my head we could have some JSON defined in a run-react-component
config, or a convention like a JSON file that is a sibling to the component file (eg. App.props.json
). This structure could also allow named keys that define different collections of props. Something like the following:
{
default: {
prop1: value,
prop2: value
},
loading: { ... },
withMaxCount: { ... }
}
Then run-react-component App.js
would use the default
key by default and could specify a different collection of props being passed down with run-react-component App.js --withMaxCount
or run-react-component App.js --loading
I had a prototype of this for React Native: depending on the file extension (.ios.js
/ .android.js
) or --platform
flag it could run either iOS or Android simulator.
Maybe it is better to rename CRA to react-cli and make it host both CRA functionality and proposed run-react-component?
The confusing part here is people will start trying to do react start
and react build
and we’ll have to explain npm scripts
. They’ll also expect code generation and unfavorably compare CRA to Ember/Angular CLIs because they provide it. We don’t want to give false impressions about project’s scope.
I think the best way to handle props with this would be just making a small new file and running that. With this sort of script it should be easy to have multiple entry points in a huge project. So if you really want to see <MyComponent foo=7 />
in the browser, you could make a new file that just contained
import React from 'React';
import MyComponent from 'wherever';
export default () => <MyComponent foo=7 />
and react-run
that. If you're working on a single component in a large codebase this seems like it could be handy. Props on the command line could be useful too, although escaping might get annoying.
Also, if we can really strip down the requirements to a single JavaScript file, there might be some interesting possibilities of finding a subset of features that works in multiple development environments. The initial use case I was thinking about for this whole thing was teaching people React through websites similarly to what we are doing on the React Native website. If you could teach people via something like codepen, where you build an app with just a single file, and the same exact thing is runnable locally, that seems like a nice way to learn. If the codepen app layout is different from the layout of a real application, it's somewhat frustrating to learn it one way and then port your knowledge over.
React Native is an interesting case too - if we could make the tooling very similar between React and React Native init for noobs, it could be easier to learn one and port your knowledge to the other.