1
\$\begingroup\$

I'm currently trying to write an app with Cordova and ReactJS. I haven't used ReactJS before, so I'm very confused by the mixed usage of Babel, JavaScript and JSX. I try to use JSX where I can because I like it, but I dislike Babel, which is used in many tutorials as well. I haven't quite understood what Babel is used for at all.

const { Router,
        Route,
        IndexRoute,
        Redirect,
        Link,
        IndexLink
      } = ReactRouter

var Header = React.createClass({
    render: function () {
        return (
            <h1 className="title">{this.props.text}</h1>
        );
    }
});

var Content = React.createClass({
    render: function() {
      return (
        <h1 className="title">{this.props.text}</h1>
      );
    }
});

var Page = React.createClass({
  getInitialState: function(){
    return {
      'title': 'Main Page' + (this.props.title ? ' - ' + this.props.title : '')
    }
  },
  render: function() {
    return (
      <div>
        <Header text={this.state.title} />
        <Content text={this.props.text} />
      </div>
    );
  }
});

var Home = React.createClass({
  getInitialState: function(){
    return {
      'title': 'Home',
      'text': <Link to="/about">About</Link>
    }
  },
  render: function(){
    return (
      <Page title={this.state.title} text={this.state.text} />
    );
  }
});

var About = React.createClass({
  getInitialState: function(){
    return {
      'title': 'About',
      'text': 'Lalal'
    }
  },
  render: function(){
    return (
      <Page title={this.state.title} text={this.state.text} />
    );
  }
});

React.render((
    <Router>
      <Route path="/" component={Home} />
      <Route path="/about" component={About} />
    </Router>
  ),
  document.getElementById('app')
);

I'm wondering if there's a better way to do this or if I'm doing everything correctly. I did my best with the classes (thinking in React stuff) and wanted to make Header a Navigation later on. Is there a good way to separate the routes, and put my views into files and components where needed?

\$\endgroup\$
3
\$\begingroup\$

I haven't quite understood what Babel is used for at all.

Babel is used for transpilation - the process of converting unsupported/future syntax into one that is supported at the moment. For instance, your destructuring assignment:

const { Router,
        Route,
        IndexRoute,
        Redirect,
        Link,
        IndexLink
      } = ReactRouter

This syntax is not supported by all browsers. Babel makes it possible by transpiling it into something supported by all browsers:

"use strict";

var _ReactRouter = ReactRouter;
var Router = _ReactRouter.Router;
var Route = _ReactRouter.Route;
var IndexRoute = _ReactRouter.IndexRoute;
var Redirect = _ReactRouter.Redirect;
var Link = _ReactRouter.Link;
var IndexLink = _ReactRouter.IndexLink;

In the same way, JSX is not natively supported by any browser. It just so happens that Babel also supports transpiling of JSX, making it the de facto tool to convert JSX to something the browser understands. This:

var Header = React.createClass({
    render: function () {
        return (
            <h1 className="title">{this.props.text}</h1>
        );
    }
});

becomes this:

"use strict";

var Header = React.createClass({
    displayName: "Header",

    render: function render() {
        return React.createElement(
            "h1",
            { className: "title" },
            this.props.text
        );
    }
});

JSX is an optional component of React. You can write it like the transpiled version. However, it somehow just feels better to construct the DOM with markup-like syntax than with a bunch of function calls and objects.

As for your code...

'title': 'Main Page' + (this.props.title ? ' - ' + this.props.title : '')

Since you have Babel (and even without Babel), you can construct strings with template strings. The above code would be as easy as:

'title': `Main Page ${title}`

And while it is possible to put logic in the point of interpolation, I suggest moving it out for readability.

Now everything looks fine in your code except when the About and Home start to use Page. The problem with this is that when Home and About sufficiently diverge in structure, Page will hinder you from easily rewriting either. For instance, what if About needed Sidebar just beside the content body. Where will you put Sidebar? You can't put it in Page otherwise, it will appear on all components using Page. You can't also put it on About just like that because it will sit at the side of the entire page content, not beside the content.

When building componentized UIs, it's best to think of the entire thing as a bunch of very small things. Think "Home has a header, content, footer, sidebar. About has a header, content, footer, and no sidebar" and not "Home and About are pages, therefore I make a Page component". The advantage of the former is if I needed a sidebar for About, I can just pop it in there. If I want to remove the header from Home, I can just take it out.

\$\endgroup\$
  • \$\begingroup\$ This is a great explanation, thank you! The only thing I'm still not sure about is the part about template strings. You can see that I'm adding a dash before the page's title if one is set, how do I do that with template strings, but keep in mind that I only want it if one is actually set? \$\endgroup\$ – TemporaryName Mar 6 '16 at 15:24
  • \$\begingroup\$ @TemporaryName You do it like they way you did, add it to title. \$\endgroup\$ – Joseph Mar 6 '16 at 15:42
1
\$\begingroup\$

Joseph the Dreamer's answer pretty much covers everything. I just have a few things to say about React practices.


Functional components

Your Content and Header components don't actually hold a state (basically, they aren't using getInitialState); they are simply presentational components (they display state).

So, rather than using React.createClass to create these components, you should use a simple function that returns what render would normally return. Here's an example:

var Content = ({text}) => <h1 className="title">{text}</h1>

This is much a much simpler and much better alternative. Here, statelessness is enforced; this is just a function that received input and gives output with zero side-effects (which is good practice). This can provide many benefits. Also, if you play on using a library like Flux or Redux in the future, this will come in handy.

Also, note the use of the Babel transpiler above:

  • ({text}) - React passes the props to functional components in the form of an argument. Here, I am using an ES6 feature to extract the text property out of the first argument.

  • var Content = (...) => - This is an arrow function. It's basically the same thing as using a function, but cleaner and easier to write. The only difference is something to do with this (you can look into it if you'd like).


Router practices

You should structure your routes in a different way:

<Router>
  <Route path="/" component={Home}>
    <Route path="about" component={About} />
  </Route>
</Router>

This way, to add more routes, all you need to do is add another sub <Route> component; this is more readable. Also, it allows you to provide default routes to redirect to when no paths are matched with <IndexRedirect>:

<Router>
  <Route path="/" component={Home}>
    <IndexRedirect to="" />
    <Route path="about" component={About} />
  </Route>
</Router>

untested right now, but should work; tell me if there are any issues.

This will make so that if a client attempts to visit any path that is not already specified under /, it will automatically go back to /.

Aside from that, you should specify a history for your <Router>. To start, you can use hashHistory (comes with the router):

<Router history={hashHistory>

However, this creates funky-looking and difficult to work with URLs. Really, you should aspire to use browserHistory, but this calls for some server configuration. Read more at the docs.


class

As Joseph the Dreamer mentioned, you are using Babel to transpile some advanced JavaScript code back into normal JavaScript. Basically, you have a lot of ES6 (and I think some ES7) features at your finger tips with this.

That being said, you should start using the class style for React components. That would look like this:

class About extends React.Component {
    getInitialState() {
        ...
    }
    render() {

    }
}

Rather than this:

var About = React.createClass({

Comment questions

Do you have a recommendation on how to organize the classes? How to import them into my JS file, etc.?

Usually, I keep it to one component/class per file. However, these components are fairly tiny and that may be unnecessary.

But, only split it up among files if you are using NodeJS and something like browserify or webpack.

If I understood correctly I could theoretically set up a 404 page with the IndexRedirect?

Theoretically, yes. Should you? Probably not.

This technically prevents a 404 page from happening (I think); if a client visits a non-existent page, they are simply re-routed to an existent one as if that's where they wanted to go all along.

Now, this isn't very good practice; you want to tell the client that that page does not exist by sending them the 404 page. I'm not quite sure how to do this, but if you look around in the docs a little bit, you might find something.

\$\endgroup\$
  • \$\begingroup\$ Thanks a lot! Do you have a recommendation on how to organize the classes? How to import them into my JS file, etc.? The Router is very confusing to me, thanks for your example there, too. If I understood correctly I could theoretically set up a 404 page with the IndexRedirect? \$\endgroup\$ – TemporaryName Mar 6 '16 at 15:31
  • \$\begingroup\$ @TemporaryName I've edited my answer to answer your questions. \$\endgroup\$ – SirPython Mar 6 '16 at 15:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.