Is there an official style guide or naming convent

2020-04-05 00:00发布

I'm setting up a React project with my team that will use mobX as state manager, plus TypeScript.

I've seen a common pattern in the casing and naming patterns in React Projects:

  1. Non-React Folders and Files: camelCase or kebab-case
  2. React (inside components folder): PascalCase

Is there a formal convention for folder/file naming in react? If not, is there a style guide on which this pattern is based? Or a reason why this one is used most of the times?

5条回答
仙女界的扛把子
2楼-- · 2020-04-05 00:25

There is not official style guide for React. But you can use most popular eslint configuration for React by AirBnb.

Read more here https://github.com/airbnb/javascript/tree/master/react

查看更多
叼着烟拽天下
3楼-- · 2020-04-05 00:32

Just to add my two cents. As others have stated, file structure is unopinionated. However, component naming is not. They should be PascalCase for React to know whether or not you're using a function, class or an HTMLelement†.

For example:

class input extends Component {...}

Bad! Why? Because React doesn't know whether or not you're trying to use the input element or the class-based component.

That's why you'll see PascalCase components:

class Input extends Component {...}

† There is one exception, where you can use dot notation. For example, if you had multiple exports and you import them all as fields, then you could do something like:

component/fields/index.js

import React, { Component } from 'react';

export class input extends Component {
  state = { value: "" };

  handleChange = ({ target: { value } }) => {
    this.setState({ value });
  };

  render = () => (
    <input type="text" value={this.state.value} onChange={this.handleChange} />
  );
}

export class textarea extends Component {
  state = { value: "" };

  handleChange = ({ target: { value } }) => {
    this.setState({ value });
  };

  render = () => (
    <textarea
      type="text"
      value={this.state.value}
      onChange={this.handleChange}
    />
  );
}

components/App/index.js

import React, { Fragment } from 'react';
import * as fields from "../fields";

const App = () => (
  <Fragment>
     <fields.input />
     <fields.textarea />
   <Fragment>
);

export default App;

As a general rule of thumb, I avoid dot notation altogether. It feels clumsy and may confuse other developers who don't know how fields is structured. Plus, I'm not a fan of stacking multiple components within 1 file and then importing them as a bunch. In addition, the file can become quite large and cumbersome to navigate and debug (more on this below).


That said, to keep my structure simple, I like to keep main directories lowercase:

├── dist // compiled application files to be served
|   ├── css
|   |   ├── main.[contenthash:8].css
|   |   └── main.[contenthash:8].css.map
|   ├── js
|   |   ├── main.[hash].js // depending on app size, this may contain multiple js files for code splitting
|   |   └── main.[hash].js.map
|   ├── media
|   |   └── [hash].[ext] // static assets like fonts and images
|   └── favicon.ico
|   └── index.html
|
├── config // supporting "webpackdevserver" configuration files
|   ├── devServer.js
|   ├── envs.js
|   ├── optimization.js
|   ├── output.js
|   ├── paths.js
|   ├── plugins.js
|   └── rules.js
|
├── public
|   ├── favicon.ico
|   └── index.html
|
├── src
|   ├── actions // redux actions
|   ├── components // stateful and stateless reusable components that just display "stuff" -- stateful components change and manipulate the UI
|   ├── containers // stateful components that utilize the reusable "components" to CRUD data and/or are connected to redux
|   ├── images
|   ├── pages // utilize components/containers to display something when visiting a "/route"
|   ├── reducers // redux reducers
|   ├── root // aka "<App />" that combines "routes", redux and other top-level supporting files into one place
|   ├── routes // assigns "pages" to a "/route"
|   ├── styles // shared and/or global styles used by all "components"
|   ├── types // redux types
|   ├── utils // supporting app files: like test setup, custom polyfills, axios configurations, ...etc
|   └── index.js // a simple file that "ReactDOM.render"s the "App"
|
├── server.js // express setup to serve the "dist" folder
└── webpack.config.js

Then within the component folder, I'll PascalCase my components to represent something like this:

└── components
    └── Input
        ├── __tests__
        |   └── Input.test.js // jest unit tests for "index.js"
        ├── index.js // all required code/styles to be exported
        └── styles.scss // styles required by "index.js"

Why this structure?

  • Reusable components that can be used wherever and whenever.
  • Everything that is related to Input is self-contained within this folder. Therefore, I can hand it off to someone and they can slot it in their application and just use it.
  • Webpack has been set up to automatically import index.js, so it's very easy to import without traversing a ton of nested files: import Input from 'components/Input'; (also, no need to specify the exact js file to use since "index.js" contains all required code).

Drawbacks:

  • You'll have a lot of small folders.
  • Compilation errors will all contain the index.js nomenclature so it can be a bit confusing at first as to what "index.js" has failed.

Another approach I used to do was:

└── components
    ├── input // lowercase name to delineate it's a "pure" function -- the actual function will be a PascalCased "Input"
    |   ├── input.test.js // jest unit tests for "input.js"
    |   ├── input.js // all required code/styles to be exported
    |   └── styles.scss // styles required by "input.js"
    |
    └── Sidebar // PascalCase because it's a "class"
        ├── Sidebar.test.js // jest unit tests for "Sidebar.js"
        ├── Sidebar.js // all required code/styles to be exported
        └── styles.scss // styles required by "Sidebar.js"

Why this structure?

  • Reusable components that can be used wherever and whenever.
  • Everything that is related to Input is self-contained within this folder. Therefore, I can hand it off to someone and they can slot it in their application and just use it.
  • Dependent on the main folder, it delineates whether the component is a function or a class.
  • When a compilation error occurs, I know exactly which file caused the error.

Drawbacks:

  • You'll have a lot of small folders.
  • Sometimes a component can change from being stateful to stateless (or vice versa), so if you are strict and stick to this naming pattern, then you'll have to update the main folder to reflect the change, which means you'll also need to update the pathing for any other files that are using this component.
  • Imports can look a bit redundant and lengthy: import Input from 'components/input/input.js';

Other general guidelines:

  • Avoid default exporting an anonymous function:

Example of a default exported anonymous function:

export default () => (
  <p>Anonymous Function</p>
);

Why? Because when testing, the function will show up in enzyme as:

<_default />

When you have multiple anonymous functions within a component, which one is which!?

<_default />
<_default />
<_default />
  • Avoid lengthy files (150 lines or less) as it becomes a pain to read/understand and even more of a pain to debug.

More often than not, I've found that most components will fall under 100 lines or so when properly optimized. Worst case scenario is I'll have to create small subcomponents to supplement the main component. But! Much easier to read and debug.

What's easier to read:

Example #1 (34 lines with supplemental child components)

Example #2 (318 lines of everything)

Example #1 mimics reading a book. Multiple pages that when glued together create an easy-to-read experience. Versus Example #2 which reads a like a mile-long scroll that can be easy to get lost in!

  • Stylesheets can be snake-case or camelCase.

This one can be confusing, but it all depends on how you're applying styles. If you're just importing the style like so:

import "./styles.css";

Then you can use snake-case:

<input className="snake-case" type="text" value="" onChange={this.handleChange} />

However, if you're using css modules, then you'll need to use camelCase:

import { camelCaseClassName } from "./styles.css";

Why? Because bundlers (like Webpack) don't support snake-case imports:

<input className={camelCaseClassName} type="text" value="" onChange={this.handleChange} />

Conclusion: There are many ways to create a folder structure with a few tips and tricks to maintain a logical flow. Just pick one that works best for you AND doesn't interfere with the person working beside you!

In other words, K.I.S.S === "Keep it simple, silly!"

查看更多
ゆ 、 Hurt°
4楼-- · 2020-04-05 00:38

At the moment i have a Folder named in PascalCase and within it i have an index.js file - this is my Component.

Any Components directly attached to the root Component i have nested in their own Folder with their own index.js. I also use dot notation to describe the nature of any files directly related to that folder e.g [descriptor].[name].[prefix]

Components/
    ComponentName/
    |---util.componentName.js
    |---constants.componentName.js
    |---styles.componentName.scss
    |---index.js
        ChildComponent1/
        |---util.childComponent1.js
        |---styles.childComponent1.scss
        |---index.js
        ChildComponent2/
        |---util.childComponent2.js
        |---styles.childComponent2.scss
        |---index.js

And for my mobx Store, because i'm less likely to have a real deep folder structure with my store modules i have one Root module-Folder with normally two js files within them Actions.js & index.js index being my main store Class that extends my Actions class. (i found that one mobx class with observable, computed and action properties got a bit cluttered).

The Store folder itself has an index.js which imports all sibling store-modules in-order to combine them into one store Object later on (needed for my project)

Store/
    StoreModule/
    |---actions.js
    |---index.js
    AnotherStoreModule/
    |---actions.js
    |---index.js
    index.js

I suppose there is no REAL right way as it's down to preference, the way above i find readable and when using tools on VSCode to find files it can make it easier when searching for specifics like "i want to see all files that are constants files" searches for constants.[component name]

查看更多
淡お忘
5楼-- · 2020-04-05 00:41

It is common across many languages to PascalCase their classes and have camelCase functions and variable names. A JS example,

function hello() {console.log('world')};

class Foo {
  say() { console.log('bar') }
}
let foo = new Foo();
foo.say();

components are often classes class Nav extends React.PureComponent and so the logical connection is to name the file containing the class similarly, resulting in matching case import statements import Nav from './Nav

You may also have a utility file, which exports a function, not a class. Again, it's nice to have matching cases import hello from './hello'

As such, you may find a common structure like

src
- App.js
- components/
  - Nav.js
- util/
  - hello.js
查看更多
家丑人穷心不美
6楼-- · 2020-04-05 00:42

There is no official guide. The reason why most projects adopt PascalCase for react components is to mimic the main export of that file. React components are PascalCased by convention, and when using jsx pascal casing becomes mandatory (only Capitalised first letter becomes mandatory actually). cameCase or kebab-case for the remaining files is just following what is also the more common preference for javascript projects in general.

查看更多
登录 后发表回答