Compile React component project practice analysis and react component project

Source: Internet
Author: User

Compile React component project practice analysis and react component project

When I first started writing React, I saw many methods for writing components. There are one hundred writing methods in one hundred tutorials. Although React itself is mature, there seems to be no "correct" way to use it. So I (the author) summarized the React usage experience summarized by our team over the years here. I hope this article will be useful to you, whether you are a beginner or veteran.

Before:

We use ES6 and ES7 syntax. If you are not clear about the differences between components and container components, we recommend that you leave a class-based component in the comments if you have any suggestions or questions.

Currently, development of React components generally uses class-based components. Next we will write our components in the same line:

import React, { Component } from 'react';import { observer } from 'mobx-react';import ExpandableForm from './ExpandableForm';import './styles/ProfileContainer.css';

I like css in javascript very much. However, the style Writing Method is too new. So we introduce css files in each component. In addition, the import introduced locally and the global import will be separated by a blank line.

Initialize State

import React, { Component } from 'react'import { observer } from 'mobx-react'import ExpandableForm from './ExpandableForm'import './styles/ProfileContainer.css'export default class ProfileContainer extends Component { state = { expanded: false }
You can use the old method in constructorIn Initialization state. For more information, see here. But we chose a clearer method.
At the same time, make sure that export default. (Note: although this is not necessarily true when redux is used ).

PropTypes and defaultProps

import React, { Component } from 'react'import { observer } from 'mobx-react'import { string, object } from 'prop-types'import ExpandableForm from './ExpandableForm'import './styles/ProfileContainer.css'export default class ProfileContainer extends Component { state = { expanded: false }  static propTypes = {  model: object.isRequired,  title: string }  static defaultProps = {  model: {   id: 0  },  title: 'Your Name' } // ...}

propTypesAnddefaultPropsIs a static property. Define the component class as much as possible, so that other developers can immediately notice when reading the code. They can serve as documents.

If you use React 15.3.0 or later, you need to introduceprop-typesPackage, instead of usingReact.PropTypes. For more information, see here.

All your components should have prop types.

Method
import React, { Component } from 'react'import { observer } from 'mobx-react'import { string, object } from 'prop-types'import ExpandableForm from './ExpandableForm'import './styles/ProfileContainer.css'export default class ProfileContainer extends Component { state = { expanded: false }  static propTypes = {  model: object.isRequired,  title: string }  static defaultProps = {  model: {   id: 0  },  title: 'Your Name' } handleSubmit = (e) => {  e.preventDefault()  this.props.model.save() }  handleNameChange = (e) => {  this.props.model.changeName(e.target.value) }  handleExpand = (e) => {  e.preventDefault()  this.setState({ expanded: !this.state.expanded }) } // ...}

In Class components, when you pass methods to sub-components, you must ensure that they are used correctly when called. This is usually done when it is passed to the child component:this.handleSubmit.bind(this).

The ES6 arrow method is much simpler. It automatically maintains the correct context (this).

Pass a method to setState

In the above example, there is such a line:

this.setState({ expanded: !this.state.expanded });
setStateIt is actually asynchronous! To improve performance, React setStateTogether. Therefore, setStateAfter that, the state may not change immediately.

ThereforesetStateYou cannot rely on the current state value. Because I didn't know that it would be a magic horse.

Solution:setStateInput a method and pass the state value before the call as a parameter to this method. Let's look at the example:

this.setState(prevState => ({ expanded: !prevState.expanded }))
Thanks to Austin Wood for help.

Disassembling Components

import React, { Component } from 'react'import { observer } from 'mobx-react'import { string, object } from 'prop-types'import ExpandableForm from './ExpandableForm'import './styles/ProfileContainer.css'export default class ProfileContainer extends Component { state = { expanded: false }  static propTypes = {  model: object.isRequired,  title: string }  static defaultProps = {  model: {   id: 0  },  title: 'Your Name' } handleSubmit = (e) => {  e.preventDefault()  this.props.model.save() }  handleNameChange = (e) => {  this.props.model.changeName(e.target.value) }  handleExpand = (e) => {  e.preventDefault()  this.setState(prevState => ({ expanded: !prevState.expanded })) }  render() {  const {   model,   title  } = this.props  return (    <ExpandableForm     onSubmit={this.handleSubmit}     expanded={this.state.expanded}     onExpand={this.handleExpand}>    <div>     

With multiple rowsprops, Each prop should occupy a single row. As in the preceding example. The best way to achieve this goal is to use a set of tools:Prettier.

Decorator)

@observerexport default class ProfileContainer extends Component {

If you know some databases, suchMobxYou can use the previous example to modify Class components. The decorator imports a Class component as a parameter into a method.

The decorator can write more flexible and readable components. If you do not want to use a decorator, you can:

class ProfileContainer extends Component { // Component code}export default observer(ProfileContainer)

Closure

Do not include closures in child components as much as possible, such:

<input type="text" value={model.name} // onChange={(e) => { model.name = e.target.value }} // ^ Not this. Use the below: onChange={this.handleChange} placeholder="Your Name"/>
NOTE: If inputIf it is a React component, this will automatically trigger its re-painting, regardless of whether other props has changed.

Consistency check is the most resource-consuming part of React. Do not add additional work here. The best way to handle the problem in the previous example is to pass in a class method, which is easier to read and debug. For example:

import React, { Component } from 'react'import { observer } from 'mobx-react'import { string, object } from 'prop-types'// Separate local imports from dependenciesimport ExpandableForm from './ExpandableForm'import './styles/ProfileContainer.css'// Use decorators if needed@observerexport default class ProfileContainer extends Component { state = { expanded: false } // Initialize state here (ES7) or in a constructor method (ES6)  // Declare propTypes as static properties as early as possible static propTypes = {  model: object.isRequired,  title: string } // Default props below propTypes static defaultProps = {  model: {   id: 0  },  title: 'Your Name' } // Use fat arrow functions for methods to preserve context (this will thus be the component instance) handleSubmit = (e) => {  e.preventDefault()  this.props.model.save() }  handleNameChange = (e) => {  this.props.model.name = e.target.value }  handleExpand = (e) => {  e.preventDefault()  this.setState(prevState => ({ expanded: !prevState.expanded })) }  render() {  // Destructure props for readability  const {   model,   title  } = this.props  return (    <ExpandableForm     onSubmit={this.handleSubmit}     expanded={this.state.expanded}     onExpand={this.handleExpand}>    // Newline props if there are more than two    <div>     

Method component

This type of component has no state, no props, and no method. They are pure components and contain the least changed content. They are often used.

PropTypes

import React from 'react'import { observer } from 'mobx-react'import { func, bool } from 'prop-types'import './styles/Form.css'ExpandableForm.propTypes = { onSubmit: func.isRequired, expanded: bool}// Component declaration

We defined it before the component declaration.propTypes.

Decompose Props and defaultProps

import React from 'react'import { observer } from 'mobx-react'import { func, bool } from 'prop-types'import './styles/Form.css'ExpandableForm.propTypes = { onSubmit: func.isRequired, expanded: bool, onExpand: func.isRequired}function ExpandableForm(props) { const formStyle = props.expanded ? {height: 'auto'} : {height: 0} return (  <form style={formStyle} onSubmit={props.onSubmit}>   {props.children}   <button onClick={props.onExpand}>Expand</button>  </form> )}

Our component is a method. Its parameter isprops. We can expand this component as follows:

import React from 'react'import { observer } from 'mobx-react'import { func, bool } from 'prop-types'import './styles/Form.css'ExpandableForm.propTypes = { onSubmit: func.isRequired, expanded: bool, onExpand: func.isRequired}function ExpandableForm({ onExpand, expanded = false, children, onSubmit }) { const formStyle = expanded ? {height: 'auto'} : {height: 0} return (  <form style={formStyle} onSubmit={onSubmit}>   {children}   <button onClick={onExpand}>Expand</button>  </form> )}

Now we can also use the default parameters to assume the role of the default props, which is quite readable. IfexpandedIt is not defined, so we set itfalse.

However, try to avoid using the following example:

const ExpandableForm = ({ onExpand, expanded, children }) => {

It looks modern, but this method is not named.

If your Babel configuration is correct, the unnamed method will not be a big problem. However, if Babel has a problem, any errors in this component are displayed as <>.

The anonymous method also causes other Jest problems. It can cause various hard-to-understand problems and has no practical advantages. We recommend that you usefunction, Less useconst.

Decoration method component

Since method components cannot use decorator, they can only be passed as parameters to other methods.

import React from 'react'import { observer } from 'mobx-react'import { func, bool } from 'prop-types'import './styles/Form.css'ExpandableForm.propTypes = { onSubmit: func.isRequired, expanded: bool, onExpand: func.isRequired}function ExpandableForm({ onExpand, expanded = false, children, onSubmit }) { const formStyle = expanded ? {height: 'auto'} : {height: 0} return (  <form style={formStyle} onSubmit={onSubmit}>   {children}   <button onClick={onExpand}>Expand</button>  </form> )}export default observer(ExpandableForm)

You can only do this:export default observer(ExpandableForm).

This is all the code of the component:

import React from 'react'import { observer } from 'mobx-react'import { func, bool } from 'prop-types'// Separate local imports from dependenciesimport './styles/Form.css'// Declare propTypes here, before the component (taking advantage of JS function hoisting)// You want these to be as visible as possibleExpandableForm.propTypes = { onSubmit: func.isRequired, expanded: bool, onExpand: func.isRequired}// Destructure props like so, and use default arguments as a way of setting defaultPropsfunction ExpandableForm({ onExpand, expanded = false, children, onSubmit }) { const formStyle = expanded ? { height: 'auto' } : { height: 0 } return (  <form style={formStyle} onSubmit={onSubmit}>   {children}   <button onClick={onExpand}>Expand</button>  </form> )}// Wrap the component instead of decorating itexport default observer(ExpandableForm)

Condition judgment

In some cases, you may make many conditional judgments:

<div id="lb-footer"> {props.downloadMode && currentImage && !currentImage.video && currentImage.blogText ? !currentImage.submitted && !currentImage.posted ? <p>Please contact us for content usage</p>  : currentImage && currentImage.selected   ? <button onClick={props.onSelectImage} className="btn btn-selected">Deselect</button>   : currentImage && currentImage.submitted    ? <button className="btn btn-submitted" disabled>Submitted</button>    : currentImage && currentImage.posted     ? <button className="btn btn-posted" disabled>Posted</button>     : <button onClick={props.onSelectImage} className="btn btn-unselected">Select post</button> }</div>

Such multi-layer condition judgment is not a good phenomenon.

A third-party library JSX-Control Statements can solve this problem. However, instead of adding a dependency, it is better to solve the problem as follows:

<div id="lb-footer"> {  (() => {   if(downloadMode && !videoSrc) {    if(isApproved && isPosted) {     return <p>Right click image and select "Save Image As.." to download</p>    } else {     return <p>Please contact us for content usage</p>    }   }   // ...  })() }</div>

Use IIFE wrapped in braces, and thenifAll expressions are included. Returns the component you want to return.

Last

Again, I hope this article will be useful to you. If you have any good comments or suggestions, please write them in the following comments. Thank you!

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.