The React specification

File organization

  • [Mandatory]. Js and. JSX files with the same name cannot be stored in the same directory.

    When using module imports, you tend not to add suffixes, and if there are files with the same name but different suffixes, the build tool will not be able to decide which module to import.

  • Component files use the consistent.js or.jsx suffix.

    All component files have a suffix of either.js or.jsx.

    You should not have a project where some components are.js files and some are.jsx.

  • [Force] Each file exposes a component as export default.

    Multiple different components are allowed in a file, but only one component is allowed to be exposed through Export Default. All other components are defined as internal components.

  • [Force] Each directory in which components are stored uses an index.js to expose all components as named exports.

    Import Foo from ‘./Foo’; Carry on.

    Components that reference other directories use import {Foo} from ‘.. /component’; Carry on.

    You are advised to use VSCode’s export-index plug-in to automatically generate index.js content.

Naming rules

  • Mandatory Component name PascalCase.

    Includes function components, all named PascalCase.

  • [Force] The component name must be the same as the file name.

    At the same time, the component name should reflect the function of the component so that you can determine which component to use by looking at the file name.

  • Higher-order components are named camelCase.

    A higher-order component is not actually a component, but a function that “generates component types” and thus follows the JavaScript function naming convention, using camelCase.

  • [Force] Use the form onXxx as the name of the property used for the callback in props.

    Using a common naming convention to distinguish the properties of the callback and non-callback parts of the props, JSX allows you to clearly see the logical interaction of a component up and down.

    For attributes of function types that are not used for callbacks, use the verb as the attribute name.

    // onClick starts with on as a callback, and renderText uses a verb for non-callback functions
    let Label = ({onClick, renderText}) = > <span onClick={onClick}>{renderText()}</span>;
    Copy the code
  • [Suggestion] Use words of the form withXxx or XXxable as names of higher-order components.

    A higher-order component is a function that adds behavior and functionality to a component, so using words in the form above helps to understand its functionality.

  • [Suggestion] Event handlers used as component methods should be named with words with business meaning, not with the onXxx form.

    // Good
    class Form {
        @autobind
        collectAndSubmitData() {
            let data = {
                name: this.state.name,
                age: this.state.age
            };
            this.props.onSubmit(data);
        }
    
        @autobind
        syncName() {
            // ...
        }
    
        @autobind
        syncAge() {
            // ...
        }
    
        render() {
            return (
                <div>
                    <label>Name:<input type="text" onChange={this.syncName} /></label>
                    <label>Age:<input type="number" onChange={this.syncAge} /></label>
                    <button type="button" onClick={this.collectAndSubmit}>submit</button>
                </div>); }}Copy the code

Component declarations

  • Use ES Class to declare components, disallow React. CreateClass.

    React V15.5.0 deprecates the React. CreateClass function.

    // Bad
    let Message = React.createClass({
        render() {
            return <span>{this.state.message}</span>; }});// Good
    class Message extends PureComponent {
        render() {
            return <span>{this.state.message}</span>; }}Copy the code
  • Components that do not use state are declared as functional components.

    Function components have a special place in React and are likely to get more internal optimizations in the future.

    // Bad
    class NextNumber {
        render() {
            return <span>{this.props.value + 1}</span>}}// Good
    let NextNumber = ({value}) = > <span>{value + 1}</span>;
    Copy the code
  • [Force] All components must declare propTypes.

    PropsTypes, while improving component robustness, is also the presence of a component-like document that helps you read and understand your code.

  • [Mandatory] Declare the corresponding value in defaultProps for all attributes that are not isRequired.

    Declaring an initial value helps in understanding the initial state of the component and reduces the overhead of propTypes verifying the type.

    For attributes that have no initial value, declare the initial value null instead of undefined.

  • [Mandatory] Declare propsTypes, contextTypes, defaultProps, and state using static property syntax if not necessary.

    Put the declaration of state in the constructor only when the initial state needs to be evaluated from props; otherwise, static property declarations are used.

  • Force to arrange methods and properties in a component in a specified order.

    Arrange the methods and properties in the component in the following order:

    1. static displayName
    2. static propTypes
    3. static contextTypes
    4. state defaultProps
    5. static state
    6. Other static properties
    7. For event handling and in the manner of attributes (onClick = e => {... }) declared methods
    8. Other instance properties
    9. constructor
    10. getChildContext
    11. componentWillMount
    12. componentDidMount
    13. shouldComponentUpdate
    14. componentWillUpdate
    15. componentDidUpdate
    16. componentWillUnmount
    17. Event handling methods
    18. Other methods
    19. render

    ShouldComponentUpdate and Render are one of the components’ easiest functions to read, so putting them at the bottom helps you quickly locate them.

  • Suggestion: Do not explicitly introduce the React object.

    Use JSX to implicitly rely on the React object in the current environment, but not explicitly in the source code, in which case add import React from ‘React ‘; Causes an unused variable to exist.

    Use the babel-plugin-react-require plugin to solve this problem, so there is no need to explicitly write import react from ‘react’; This statement.

  • [Suggestion] Use arrow functions to declare function components.

    Arrow functions have a cleaner syntax (no need for the function keyword) and can omit the extra indentation caused by return when there is only one statement.

  • Suggestion: Add the displayName attribute when an advanced component returns a new component type.

    Also declare the existence of higher-order components on displayName.

    // Good
    let asPureComponent = Component= > {
        let componentName = Component.displayName || Component.name || 'UnknownComponent';
        return class extends PureComponent {
            static displayName = `asPure(${componentName}) `
    
            render() {
                return <Component {. this.props} / >; }}; };Copy the code

Component implementation

  • All components except the top-level or routing components are conceptually implemented as Pure components.

    This rule does not require components to inherit from PureComponent. “conceptually PureComponent” means that a component should render the same results without changing its props and state. ShouldComponentUpdate should return false.

    A typical impure component uses functions such as random numbers or dates:

    let RandomNumber = () = > <span>{Math.random()}</span>;
    let Clock = () = > <span>{Date.time()}</span>;
    Copy the code

    Impure components have upward contagiousness, that is, a component that contains impure components must also be impure components, ascending the component tree. Because impure components cannot be optimized for rendering performance with shouldComponentUpdate and are contagious, avoid using them in non-top-level or routed components.

    If you need to use impure data such as random numbers, dates, etc. at a node in the component tree, the top-level component should generate the value and pass it down through props. For systems that use application state management, such as Redux, you can store values in the application state (for example, Redux generates these values using Action Creator and updates them into the store using Action and Reducer).

  • [Mandatory] Disallow shouldComponentUpdate for components that inherit from PureComponent.

    The React implementation does not implement shouldComponentUpdate directly. Instead, it adds an isReactPureComponent flag. The CompositeComponent implements the associated logic by recognizing this tag. So on PureComponent custom shouldComponentUpdate and unable to enjoy the super. The logic of shouldComponentUpdate reuse, also makes the inheritance relationship.

  • Implement shouldComponentUpdate for pure components that do not inherit from PureComponent.

    The shouldComponentUpdate method plays a critical role in React performance. Pure components must be able to render using props and state changes, so if a component is pure and shouldComponentUpdate does not inherit it, Should have its own shouldComponentUpdate implementation to reduce unnecessary rendering.

  • [Suggestion] Add PureComponent capability to function components.

    Function components are not necessarily pure components, so shouldComponentUpdate should return true; This can lead to extra meaningless rendering, so it is recommended to add shouldComponentUpdate logic to the higher-order component.

    The react-pure-Stateless component library is recommended for this function.

  • [Suggestion] Use @autobind tobind event handling methods to this.

    Since PureComponent uses shallowEqual to render, binding this in JSX with bind or arrow causes the subcomponent to get a new reference every time. This breaks shouldComponentUpdate logic. Meaningless repeated rendering is introduced, so you need to bind the event handler to this before the Render call and get the same reference in every Render call.

    There are two popular ways to pre-bind this, one using class attribute syntax:

    class Foo {
        onClick = e= > {
            // ...}};Copy the code

    The second decorator uses @autobind:

    class Foo {
        @autobind
        onClick(e) {
            // ...}}Copy the code

    Using class attribute syntax, while avoiding the introduction of an autobind implementation, has certain drawbacks:

    1. Not easy for beginners to understand inside functionsthisThe definition.
    2. Cannot use other decorators on functions (e.gmemoize,deprecatedOr examine the relevant logic, etc.).

    Therefore, it is recommended to use the @autobind decorator to implement the prior binding of this, and to use the related decorator implementation provided by the Core-decorators library.

JSX

  • [Force] Use self-closing syntax for non-DOM components that have no child nodes.

    DOM nodes are closed according to the relevant rules of HTML coding specification, where void element uses self-closing syntax.

    // Bad
    <Foo></Foo>
    
    // Good
    <Foo />
    Copy the code
  • [Force] Indent start and end labels at the same level.

    For tags preceded by other statements (as in the case of return, use parentheses for line breaks and indentation).

    // Bad
    class Message {
        render() {
            return <div>
                <span>Hello World</span>
            </div>; }}// Good
    class Message {
        render() {
            return (
                <div>
                    <span>Hello World</span>
                </div>); }}Copy the code

    For function components that return directly, we can use parentheses without the curly braces and return keyword:

    let Message = () = > (
        <div>
            <span>Hello World</span>
        </div>
    );
    Copy the code
  • [Mandatory] Line breaks are required for multiple attributes, starting with the first attribute and one line for each attribute.

    // There are no child nodes
    <SomeComponent
        longProp={longProp}
        anotherLongProp={anotherLongProp}
    />
    
    // There are child nodes
    <SomeComponent
        longProp={longProp}
        anotherLongProp={anotherLongProp}
    >
        <SomeChild />
        <SomeChild />
    </SomeComponent>
    Copy the code
  • [Enforces] Double quotes (“) for attributes that take string literals as values, and single quotes (‘) for strings in other types of expressions.

    // Bad
    <Foo bar='bar' />
    <Foo style={{width: "20px}} "/ >
    
    // Good
    <Foo bar="bar" />
    <Foo style={{width: '20px'}} / >
    Copy the code
  • [Mandatory] Add a space before /> of the self-closing label.

    // Bad
    <Foo bar="bar"/>
    <Foo bar="bar"  />
    
    // Good
    <Foo bar="bar" />
    Copy the code
  • [Mandatory] Omit the value part for properties with a value of true.

    // Bad
    <Foo visible={true} / >// Good
    <Foo visible />
    Copy the code
  • [Mandatory] Provide a unique identifier as the value of the key attribute when the key is required. Do not use attributes (such as indexes) that may change.

    The key attribute is an important attribute when React updates the list. If the key attribute changes, rendering performance and correctness cannot be guaranteed.

    // Bad
    {list.map((item, index) = > <Foo key={index} {. item} / >)}
    
    // Good
    {list.map(item= > <Foo key={item.id} {. item} / >)}
    Copy the code
  • Avoid using object and function expressions directly in JSX attribute values.

    PureComponent uses shallowEqual to compare props and state to determine if rendering is required. Using objects and function expressions in JSX property values will result in different object references each time and shallowEqual will return false, resulting in unnecessary rendering.

    // Bad
    class WarnButton {
        alertMessage(message) {
            alert(message);
        }
    
        render() {
            return <button type="button" onClick={()= >Enclosing alertMessage (enclosing props. Message)} > prompt</button>}}// Good
    class WarnButton {
        @autobind
        alertMessage() {
            alert(this.props.message);
        }
    
        render() {
            return <button type="button" onClick={this.alertMessage}>prompt</button>}}Copy the code
  • [Recommendation] Keep the JSX hierarchy within 3 layers.

    JSX provides a portable form of component-based reuse, so large and complex structures can be nicely broken down by encapsulating part of the structure as a functional component. Too deep a structure can lead to excessive indentation and reduced readability. Just like controlling the number of lines of code and branch levels within functions, controlling the level of JSX can improve the maintainability of your code.

    // Bad
    let List = ({items}) = > (
        <ul>
            {
                items.map(item => (
                    <li>
                        <header>
                            <h3>{item.title}</h3>
                            <span>{item.subtitle}</span>
                        </header>
                        <section>{item.content}</section>
                        <footer>
                            <span>{item.author}</span>@<time>{item.postTime}</time>
                        </footer>
                    </li>))}</ul>
    );
    
    // Good
    let Header = ({title, subtitle}) = > (
        <header>
            <h3>{title}</h3>
            <span>{subtitle}</span>
        </header>
    );
    
    let Content = ({content}) = > <section>{content}</section>;
    
    let Footer = ({author, postTime}) = > (
        <footer>
            <span>{author}</span>@<time>{postTime}</time>
        </footer>
    );
    
    let Item = item= > (
        <div>
            <Header {. item} / >
            <Content {. item} / >
            <Footer {. item} / >
        </div>
    );
    
    let List = ({items}) = > (
        <ul>
            {items.map(Item)}
        </ul>
    );
    Copy the code

See the React specification