Cấu trúc thư mục của một dự án React đúng chuẩn https://wikifin.net

Cách viết component chuẩn trong React. Mục tiêu là làm thế nào cho dự án Bất Động Sản dễ quản trị, cũng như dễ hiểu để cộng tác thao tác với nhau, cũng như tăng tính tái sử dụng của một component React, việc vận dụng theo hay không thì tùy bạn – tất yếu, nhưng nếu bạn không sử dụng một quy luật nào đó thống nhất, một ngày không xa bạn sẽ cảm thấy không quản trị hết được dự án Bất Động Sản của mình khi ngày nó càng phình ra .
Một dự án Bất Động Sản web app đơn thuần nếu muốn đạt mức độ thành phẩm thì sẽ có từ 20-30 components, và theo tiêu chuẩn component càng nhỏ càng tốt vì nó có tính tái sử dụng cao, với cấu trúc thư mục bên dưới webapp hoàn toàn có thể chứa khoảng chừng 200 – 300 components. Đối với mức 20,000 React components thì phải hỏi chính đội ngũ của Facebook mới được, chứ mình thì chịu ! ( trích dẫn “ the Facebook codebase has over 20,000 React components, and that’s not even counting React Native ” )

Cấu trúc thư mục của một dự án React

/actions/...
/components/common/Link.js
/components/common/...
/components/forms/TextBox.js
/components/forms/TextBox.container.js /* Container component */
/components/forms/TextBox.res/style.css
/components/forms/TextBox.locale/vi-VN/...
/components/forms/...
/components/layout/App.js - The top-level React component
/components/layout/App.res/style.css
/components/layout/App.locale/en-US/...
/components/layout/Navigation.js
/components/layout/Navigation.res/style.css
/components/layout/Navigation.res/data.json
/components/layout/Navigation.locale/en-US/...
/components/layout/...
/components/pages/Home.js
/components/pages/Home.css
/components/pages/Account/index.js
/components/pages/Account/index.css
/components/pages/...
/core/...
/constants/...
/helpers/...
/reducers/...
/stores/...

Về Component có nhận tài liệu từ sever hoặc từ store và nhận tài liệu từ thao tác người dùng, mình chia làm 2 component với 2 tính năng riêng không liên quan gì đến nhau :

  • Container: nhận dữ liệu từ server, component này không render và cũng không nhận bất kỳ thao tác nào của người dùng
  • Presentation: chỉ đảm nhiệm việc render, chỉ nhận dữ liệu thông qua props, không có state

Cách tiếp cận này (Container component và Presentation) được Dan Abramov (tác giả Redux và đang là thành viên của React) giới thiệu qua bài viết: Smart and Dump components, và cấu trúc thư mục thì dựa theo Gist này.

Mình sử dụng component stateful và stateless độc lập như trên để dễ quản lý luồng dữ liệu. Trong cấu trúc trên có sử dụng Redux, tuy nhiên chỉ để tham khảo là chính, nếu bạn có sử dụng Redux thì có thêm các thư mục reducersstores, actions. Còn thư mục core thì có thể chứa business logic, helpers dùng để chứa các hàm hỗ trợ, constants để chứa các biến hằng số.

Thật ra, nếu bạn không sử dụng Redux, hoàn toàn có thể chia làm 3 components – đã vận dụng thấy rất ổn :

  • Container: như trên
  • Presentation: như trên
  • Interactive: nhận các thao tác từ người dùng, component này sẽ khai báo các hàm handleClick, handleChange,…và truyền cho presentation component thông qua props.

Với cách tổ chức triển khai 3 components như trên thì ưu điểm là phân rõ rạch ròi trách nhiệm cho từng component : lấy tài liệu, xuất dữ liệu và tương tác người dùng. Khuyết điểm của cách tiếp cận này là tài liệu hoàn toàn có thể bị trùng lặp ở component Interactive, vì bản thân nó không sử dụng mà truyền xuống cho Presentation, tuy nhiên đây không phải quá tệ để cho cách tổ chức triển khai component dễ hiểu và quản trị luồng tài liệu, cách này được xem là tựa như actions -> store < => reduce .

Trong thư mục chính components, được chia làm các thư mục con, các thư mục con này cũng là các react component, tuy nhiên được phân loại theo từng cấp độ lớn nhỏ: pages chứa các component theo phân loại chức năng trang, trong một trang (page component) chứa rất nhiều component nhỏ khác và được bọc trong layout component (layouts)

Cách viết một component React đúng chuẩn ….Airbnb

Tham khảo tại đây : https://github.com/airbnb/javascript/tree/master/react
Học theo những kinh nghiệm tay nghề của những đàn anh là cách tiếp cận mưu trí nhất :), bạn hãy đọc sơ lược, nếu chưa nhớ rõ vận dụng hết những thứ trong đó thì cứ làm theo từng cái thôi. Nhưng tốt hơn hết là đọc 1 lượt kỹ càng, sau đó viết 2-3 components, cái này quên quen bay qua coi lại, chừng 5-6 components là bạn sẽ quen cách tổ chức triển khai và viết component đúng chuẩn rồi .

Hãy xây dựng component từng dòng một.

Nạp CSS

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

import React, { Component } from 'react'

import { observer } from 'mobx-react'

import ExpandableForm from './ExpandableForm'

import './styles/ProfileContainer.css'

Về mặt kim chỉ nan thì tôi thích đặt CSS trong JavaScript. Nhưng nó vẫn là một sáng tạo độc đáo mới. Trước khi sáng tạo độc đáo đó hoàn thành xong, tất cả chúng ta vẫn nhập một file CSS vào mỗi component .
Chúng tôi cũng tách dependency imports từ local imports bằng một dòng mới .

Khởi tạo State

Bạn cũng hoàn toàn có thể sử dụng cách tiếp cận cũ hơn để khởi tạo state trong constructor. Thêm vào đó ở đây. Chúng tôi thích cách ngăn nắp hơn .

importReact,{Component}from' react '

import{observer}from' mobx-react '

importExpandableFormfrom'. / ExpandableForm '

import'. / styles / ProfileContainer. css '

exportdefaultclassProfileContainer

extends

Component{ state={expanded:false}

Chúng tôi cũng bảo vệ export class mặc định .

propTypes và 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'

  }

PropTypes và defaultProps là những thuộc tính tĩnh, được khai báo càng cao càng tốt trong component code. Chúng phải được hiển thị ngay cho những dev khác đọc file vì chúng được dùng làm tài liệu .
Nếu sử dụng React 15.3.0 hoặc cao hơn, hãy sử dụng gói prop-types thay vì React. PropTypes bởi cấu trúc độc lạ ( đương nhiên ) .
Tất cả những component của bạn nên có propTypes .

Methods

Với những class component, khi bạn chuyển những method thành những tiểu hợp phần, bạn phải bảo vệ rằng chúng có quyền này khi chúng được gọi. Điều này thường đạt được bằng cách truyền this. handleSubmit. bind ( this ) vào tiểu hợp phần .

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 })

  }

Chúng tôi nghĩ rằng cách tiếp cận này gọn và thuận tiện hơn mà vẫn giúp sửa chữa thay thế context tự động hóa trải qua công dụng arrow của ES6 .

Bỏ qua một hàm trong setState 

Trong ví dụ này, chúng tôi triển khai việc này :

this.setState({ expanded: !this.state.expanded })

Đây là bí hiểm về setState, nó thực sự không đồng điệu. React đổi khác state vì những nguyên do hiệu suất, do đó state không hề biến hóa ngay sau khi setState được gọi .
Điều đó có nghĩa là bạn không nên dựa vào state hiện tại khi gọi setState – vì bạn không hề chắc như đinh state đó sẽ là gì !
Đây là giải pháp – bỏ lỡ một hàm trong setState, với state trước đó như thể một đối số .

this.setState(prevState => ({ expanded: !prevState.expanded }))

Destructuring các Props

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 (

      

        

{title}

) } }

Các component với nhiều props cần phải có mỗi bước trên một dòng mới, như ở trên .

Decorators

@observer

export default class ProfileContainer extends Component {

Nếu bạn đang sử dụng một cái gì đó như mobx, bạn hoàn toàn có thể decorator những class component của bạn như vậy – tương tự như như chuyển component vào một hàm .
Decorator là cách linh hoạt và hoàn toàn có thể đọc được việc sửa đổi component tính năng. Chúng tôi sử dụng chúng thoáng đãng, với mobx và thư viện những quy mô mobx của tất cả chúng ta .
Nếu bạn không muốn sử dụng decorator, hãy làm như sau :

class ProfileContainer extends Component {

  // Component code

}

export default observer(ProfileContainer)

Closures

Tránh chuyển closures mới thành những component con, như sau :

 { model.name = e.target.value }}

            // ^ Not this. Use the below:

            onChange={this.handleChange}

            placeholder="Your Name"/>

Đây là nguyên do tại sao : mỗi khi component gốc được hiển thị, một tính năng mới được tạo và truyền cho nguồn vào .
Nếu nguồn vào là một component React, điều này sẽ tự động hóa kích hoạt nó để render lại, bất kể những props khác của nó đã biến hóa như thế nào .
Reconciliation là phần tốt nhất của React. Đừng khiến nó trở nên khó hơn mức thiết yếu ! Cộng thêm, bỏ lỡ class method khiến nó dễ đọc, debug và dễ biến hóa hơn .
Đây là component khá đầy đủ của chúng tôi :

import React, { Component } from 'react'

import { observer } from 'mobx-react'

import { string, object } from 'prop-types'

// Separate local imports from dependencies

import ExpandableForm from './ExpandableForm'

import './styles/ProfileContainer.css'


// Use decorators if needed

@observer

export 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 (

      

        // Newline props if there are more than two

        

{title}

{ model.name = e.target.value }} // Avoid creating new closures in the render method- use methods like below onChange={this.handleNameChange} placeholder="Your Name"/>
) } }

Những Component hàm

Các component này không có state và không có method. Chúng là nguyên mẫu và thuận tiện để hiểu được. Sử dụng chúng càng tiếp tục càng tốt .

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

Ở đây, tất cả chúng ta chỉ định những propTypes trước khai báo component, do đó chúng hoàn toàn có thể nhìn thấy ngay lập tức. Chúng tôi hoàn toàn có thể làm điều này vì tính năng hoist của JavaScript .

Destructure Props và 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 (

    
{props.children}
) }

Component của tất cả chúng ta là một công dụng, lấy những props của nó làm đối số của nó. Chúng ta hoàn toàn có thể lan rộng ra chúng như sau :

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 (

    
{children}
) }

Lưu ý rằng chúng tôi cũng có thể sử dụng đối số mặc định để hoạt động như defaultProp. Nếu mở rộng không xác định, chúng tôi sẽ đặt nó là false. (Trong một ví dụ bắt buộc, vì đó là một boolean, nhưng đó là cách tốt để tránh lỗi ‘Cannot read of undefined’ với các đối tượng).

Tránh cú pháp ES6 sau:

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

Nhìn có vẻ như rất tân tiến, nhưng tính năng ở đây thực sự không có tên .

Việc thiếu tên sẽ không là vấn đề nếu Babel của bạn được thiết lập đúng – nhưng nếu không, bất kỳ lỗi nào cũng có thể xuất hiện trong <> và đó là điều khủng khiếp khi debug.

Các tính năng không tên cũng hoàn toàn có thể gây ra yếu tố với Jest, thư viện kiểm thử của Javascript. Do yếu tố khó khăn vất vả để hiểu lỗi ( và sự thiếu quyền lợi thực sự ), chúng tôi khuyên bạn nên sử dụng function thay vì const .

Wrapping

Vì bạn không hề sử dụng decorator với những component công dụng, bạn chỉ cần bỏ lỡ nó như một đối số :

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 (

    
{children}
) } export default observer(ExpandableForm)

Đây là component đầy đủ của chúng tôi:

import React from 'react'

import { observer } from 'mobx-react'

import { func, bool } from 'prop-types'

// Separate local imports from dependencies

import './styles/Form.css'


// Declare propTypes here, before the component (taking advantage of JS function hoisting)

// You want these to be as visible as possible

ExpandableForm.propTypes = {

  onSubmit: func.isRequired,

  expanded: bool,

  onExpand: func.isRequired

}


// Destructure props like so, and use default arguments as a way of setting defaultProps

function ExpandableForm({ onExpand, expanded = false, children, onSubmit }) {

  const formStyle = expanded ? { height: 'auto' } : { height: 0 }

  return (

    
{children}
) } // Wrap the component instead of decorating it export default observer(ExpandableForm)

Điều kiện trong JSX

Rất hoàn toàn có thể bạn đang thực thi rất nhiều việc render có điều kiện kèm theo. Dưới đây là những gì bạn muốn tránh khỏi :

Điều kiện trong JSX

Tuy nhiên, đặt chúng lồng vào nhau không phải là một ý tưởng hay.

Có một số ít thư viện xử lý yếu tố này ( JSX-Control Statements ), nhưng thay vì trình làng một dependency khác, chúng tôi xử lý theo cách tiếp cận này cho những điều kiện kèm theo phức tạp :

Điều kiện trong JSX

Sử dụng dấu ngoặc nhọn đóng gói một IIFE và sau đó đưa câu lệnh if của bạn vào bên trong, trả về bất kể điều gì bạn muốn render. Lưu ý rằng IIFE như thế này hoàn toàn có thể gặp công dụng phụ, nhưng trong nhiều trường hợp nó sẽ không đủ sức ảnh hưởng tác động làm mất đi những factor dễ hiểu .

Source: https://wikifin.net
Category: Blog

Leave a Comment

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *