For many newcomers to React, the resources available online are mostly simple tutorials that teach you how to use React, but don’t show you how to gracefully organize and write React code in real projects. A Google search of the Chinese “React Best Practices” shows that the first two pages are almost all translations of the same foreign article… Therefore, I summarized some pits I used to use React in the past project, and also sorted out some other people’s opinions, hoping to be helpful to some React users.

The React with AJAX

React handles only the View layer, which itself does not involve network requests /AJAX, so we need to consider two issues here:

  • First, what technology is used to get data from the server?

  • Second, where should the obtained data be placed in the React component?

React provides a solution: Load Initial Data via AJAX

Make an Ajax request to componentDidMount() using jQuery’s Ajax method, store the data in the component’s state, and call the setState method to update the UI. If the data is retrieved asynchronously, cancel sending the request in componentWillUnmount.

Importing the entire jQuery library just to use jQuery’s Ajax methods is wasteful and adds to the size of the entire application. So what other options do we have? In fact there are many: fetch(), fetch polyfill, Axios… The one that needs our attention most is window.fetch(), which is a clean, standardized Ajax API for javascript. It is already available in Chrome and Firefox, and fetch Polyfill can be used if you want to work with other browsers.

The React documentation only tells us how to retrieve data from the server using Ajax in a single component, but it doesn’t tell us which components to store data in in a complete project. This part of the project can become confusing and difficult to maintain if there is no specification. Here are three good practices:

1. All data requests and management reside in a single root component

Have the parent/root component send all ajax requests centrally, store the data from the server in the component’s state, and pass the data to the child components through props. This approach is mainly for small applications where component trees are not very complex. The disadvantage is that as the component tree becomes more hierarchical, the data needs to be passed to the child components layer by layer, which is cumbersome to write and poor performance.

2. Set up multiple container components to handle data requests and management

This is similar to the first approach, except that multiple container components are set up to handle data requests and state management. We need to distinguish between two different types of components, a Presentational component and a Container Component. The presentation component does not have any state of its own, and all data is retrieved from the container component, where ajax requests are sent. For a more detailed description of both, read this article: Presentational and Container Components

A concrete example:

Let’s say we need to present a user’s name and profileImage. We first create a presentation component that accepts two Props: name and profileImage. There is no Ajax code in this component.

Then create a container component that takes a userId parameter, sends an Ajax request to retrieve data from the server in state, and passes it to the component via props.

3. Use of Redux or Relay

Redux manages state and data, and Ajax gets the data from the server, so it’s obvious that when you use Redux, you should hand over all network requests to Redux. Specifically, it should be placed in Async Actions. With other Flux-like libraries, the solution is similar, sending network requests in actions.

Relay is an official library from Facebook. To use it, we just need to declare the data needed by the component via GraphQL, and Relay will automatically pass the downloaded data down through props. To use Relay, you need to have a GraphQL server…

The organizational structure of a standard component

1 class definition
    1.1 constructor
        1.1.1 event handlers
    1.2 'component' lifecycle events
    1.3 getters
    1.4 render
2 defaultProps
3 proptypesCopy the code

Example:

class Person extends React.Component { constructor (props) { super(props); this.state = { smiling: false }; this.handleClick = () => { this.setState({smiling: ! this.state.smiling}); }; } componentWillMount () { // add event listeners (Flux Store, WebSocket, document, etc.) }, componentDidMount () { // React.getDOMNode() }, componentWillUnmount () { // remove event listeners (Flux Store, WebSocket, document, etc.) }, get smilingMessage () { return (this.state.smiling) ? "is smiling" : ""; } render () { return (
       
{this.props.name} {this.smilingMessage}
); }, } Person.defaultProps = { name: 'Guest' }; Person.propTypes = { name: React.PropTypes.string };Copy the code

Source of the above sample code: github.com/planningcen…

Use PropTypes and getDefaultProps()

  1. Always write PropTypes, don’t omit them for convenience

  2. If a Props is not requied, be sure to set it in getDefaultProps

    React.PropTypes is used to verify that the component is receiving the correct props data type. If it is not, a warning appears on the console. For performance reasons, this API is only used in production environments.

Basic usage:

propTypes: { myArray: React.PropTypes.array, myBool: React.PropTypes.bool, myFunc: React.PropTypes.func, myNumber: React.PropTypes.number, myString: The React. PropTypes. String,  // You can chain any of the above with `isRequired` to make sure a warning // is shown if the prop isn't provided. requiredFunc: React.PropTypes.func.isRequired }Copy the code

What if instead of the above types, we want objects with complex structures? Like this one:

{
  text: 'hello world',
  numbers: [5, 2, 7, 9],
}Copy the code

Of course, we can use React.PropTypes. Object directly, but we can’t verify the data inside the object.

propTypes: {
  myObject: React.PropTypes.object,
}Copy the code

Advanced use methods: Shape () and arrayOf()

propTypes: {
  myObject: React.PropTypes.shape({
    text: React.PropTypes.string,
    numbers: React.PropTypes.arrayOf(React.PropTypes.number),
  })
}Copy the code

Here is a more complex Props:

[
  {
    name: 'Zachary He',
    age: 13,
    married: true,
  },
  {
    name: 'Alice Yo',
    name: 17,
  },
  {
    name: 'Jonyu Me',
    age: 20,
    married: false,
  }
]Copy the code

Combined with the above, it should be easy to write:

propTypes: {
    myArray: React.PropTypes.arrayOf(
        React.propTypes.shape({
            name: React.propTypes.string.isRequired,
            age: React.propTypes.number.isRequired,
            married: React.propTypes.bool
        })
    )
}Copy the code

Hand over the calculations and the conditioningrender()Methods!

1. The props cannot be displayed in the state of the component
 // BAD:
  constructor (props) {
    this.state = {
      fullName: `${props.firstName} ${props.lastName}`
    };
  }

  render () {
    var fullName = this.state.fullName;
    return (
      
       

{fullName}

); }Copy the code
// GOOD: 
render () {
  var fullName = `${this.props.firstName} ${this.props.lastName}`;
}Copy the code

Of course, complex display logic should also be avoided in render(), as that can result in the render() method becoming bloated and unelegant. We can remove some of the complex logic using the helper function.

// GOOD: helper function
renderFullName () {
  return `${this.props.firstName} ${this.props.lastName}`;
}

render () {
  var fullName = this.renderFullName();
}Copy the code
2. Keep state simple and do not have calculated state
// WRONG:
  constructor (props) {
    this.state = {
      listItems: [1, 2, 3, 4, 5, 6],
      itemsNum: this.state.listItems.length
    };
  }
  render() {
      return (
          
       
{this.state.itemsNum}
)}Copy the code
// Right:
render () {
  var itemsNum = this.state.listItems.length;
}Copy the code
Render (); render(); render(); render(
// BAD: 
renderSmilingStatement () {
    if (this.state.isSmiling) {
        return is smiling;
    }else {
        return '';
    }
},

render () {
  return 
       
{this.props.name}{this.renderSmilingStatement()}
; }Copy the code
// GOOD: 
render () {
  return (
    
       
{this.props.name} {(this.state.smiling) ? is smiling : null }
); }Copy the code
4. If booleans can’t handle it, leave it to IIFE

Immediately-invoked function expression

return (
  
    

Color

Name

{this.state.color || "white"}

Hex

{(() => { switch (this.state.color) { case "red": return "#FF0000"; case "green": return "#00FF00"; case "blue": return "#0000FF"; default: return "#FFFFFF"; }}}) ()

);Copy the code
5. Don’t write display Logic incomponentWillReceivePropsorcomponentWillMountRender (), move them all to render().

How do I handle classNames dynamically

1. Use booleans
// BAD: constructor () { this.state = { classes: [] }; } handleClick () { var classes = this.state.classes; var index = classes.indexOf('active'); if (index ! = -1) { classes.splice(index, 1); } else { classes.push('active'); } this.setState({ classes: classes }); }Copy the code
// GOOD: constructor () { this.state = { isActive: false }; } handleClick () { this.setState({ isActive: ! this.state.isActive }); }Copy the code
2. UseclassnamesThis widget concatenates classNames:
// BEFORE: var Button = React.createClass({ render () { var btnClass = 'btn'; if (this.state.isPressed) btnClass += ' btn-pressed'; else if (this.state.isHovered) btnClass += ' btn-over'; return {this.props.label}; }});Copy the code
// AFTER: var classNames = require(' classNames '); var Button = React.createClass({ render () { var btnClass = classNames({ 'btn': true, 'btn-pressed': this.state.isPressed, 'btn-over': ! this.state.isPressed && this.state.isHovered }); return {this.props.label}; }});Copy the code

To be continued…