React組件設想技能

React組件設想

組件分類

展現組件和容器組件

展現組件容器組件
關注事物的展現關注事物怎樣事變
可以包含展現和容器組件,而且平常會有DOM標籤和css款式可以包含展現和容器組件,而且不會有DOM標籤和css款式
常常許可經由歷程this.props.children通報供應數據和行動給容器組件或許展現組件
對第三方沒有任何依靠,比方store 或許 flux action挪用flux action 而且供應他們的回調給展現組件
不要指定數據怎樣加載和變化作為數據源,一般採納較高階的組件,而不是自身寫,比方React Reduxconnect(), Relay的createContainer(), Flux UtilsContainer.create()
僅經由歷程屬性獵取數據和回調null
很少有自身的狀況,縱然有,也是自身的UI狀況null
除非他們須要的自身的狀況,性命周期,或機能優化才會被寫為功用組件null

下面是一個可以會常常寫的組件,批評列表組件,數據交互和展現都放到了一個組件內里。

// CommentList.js
class CommentList extends React.Component {
  constructor() {
    super();
    this.state = { comments: [] }
  }
  componentDidMount() {
    $.ajax({
      url: "/my-comments.json",
      dataType: 'json',
      success: function(comments) {
        this.setState({comments: comments});
      }.bind(this)
    });
  }
  render() {
    return <ul> {this.state.comments.map(renderComment)} </ul>;
  }
  renderComment({body, author}) {
    return <li>{body}—{author}</li>;
  }
}

我們對上面的組件舉行拆分,把他拆分紅容器組件 CommentListContainer.js 和展現組件 CommentList

// CommentListContainer.js
class CommentListContainer extends React.Component {
  constructor() {
    super();
    this.state = { comments: [] }
  }
  componentDidMount() {
    $.ajax({
      url: "/my-comments.json",
      dataType: 'json',
      success: function(comments) {
        this.setState({comments: comments});
      }.bind(this)
    });
  }
  render() {
    return <CommentList comments={this.state.comments} />;
  }
}


// CommentList.js
class CommentList extends React.Component {
  constructor(props) {
    super(props);
  }
  render() { 
    return <ul> {this.props.comments.map(renderComment)} </ul>;
  }
  renderComment({body, author}) {
    return <li>{body}—{author}</li>;
  }
}

上風:

  • 展現和容器更好的星散,更好的邃曉運用程序和UI
  • 重用性高,展現組件可以用於多個差別的state數據源
  • 展現組件就是你的調色板,可以把他們放到零丁的頁面,在不影響運用程序的狀況下,讓設想師調解UI
  • 迫使你星散標籤,到達更高的可用性

有狀況組件和無狀況組件

下面是一個最簡樸的無狀況組件的例子:

function HelloComponent(props, /* context */) {
  return <div>Hello {props.name}</div>
}
ReactDOM.render(<HelloComponent name="Sebastian" />, mountNode)

可以看到,底本須要寫“類”定義(React.createClass 或許 class YourComponent extends React.Component)來建立自身組件的定義(有狀況組件),現在被精簡成了只寫一個 render 函數。更值得一提的是,由於僅僅是一個無狀況函數,React 在襯着的時刻也省掉了將“組件類” 實例化的歷程。

連繫 ES6 的解構賦值,可以讓代碼更精簡。比方下面這個 Input 組件:

function Input({ label, name, value, ...props }, { defaultTheme }) {
  const { theme, autoFocus, ...rootProps } = props
  return (
    <label
      htmlFor={name}
      children={label || defaultLabel}
      {...rootProps}
    >
    <input
      name={name}
      type="text"
      value={value || ''}
      theme={theme || defaultTheme}
      {...props}
    />
  )}
Input.contextTypes = {defaultTheme: React.PropTypes.object};

無狀況組件不像上述兩種要領在挪用時會建立新實例,它建立時始終堅持了一個實例,防止了不必要的搜檢和內存分派,做到了內部優化。

無狀況組件不支撐 “ref”

高階組件

高階組件經由歷程函數和閉包,轉變已有組件的行動,本質上就是 Decorator 形式在 React 的一種完成。

當寫着寫着無狀況組件的時刻,有一天遽然發明須要狀況處置懲罰了,那末無需完整返工:)
每每我們須要狀況的時刻,這個需求是可以重用的。

高階組件加無狀況組件,則大大增強了全部代碼的可測試性和可維護性。同時不停“誘使”我們寫出組合性更好的代碼。

高階函數

function welcome() {
    let username = localStorage.getItem('username');
    console.log('welcome ' + username);
}

function goodbey() {
    let username = localStorage.getItem('username');
    console.log('goodbey ' + username);
}

welcome();
goodbey();

我們發明兩個函數有一句代碼是一樣的,這叫冗餘唉。(日常平凡可以會有一大段代碼的冗餘)。

下面我們要寫一个中心函數,讀取username,他來擔任把username通報給兩個函數。

function welcome(username) {
    console.log('welcome ' + username);
}

function goodbey(username) {
    console.log('goodbey ' + username);
}

function wrapWithUsername(wrappedFunc) {
    let newFunc = () => {
        let username = localStorage.getItem('username');
        wrappedFunc(username);
    };
    return newFunc;
}

welcome = wrapWithUsername(welcome);
goodbey = wrapWithUsername(goodbey);

welcome();
goodbey();

好了,我們內里的 wrapWithUsername 函數就是一個“高階函數”。
他做了什麼?他幫我們處置懲罰了 username,通報給目的函數。我們挪用終究的函數 welcome的時刻,基本不必體貼 username是怎樣來的。

聞一知十的高階組件

下面是兩個冗餘的組件。

import React, {Component} from 'react'

class Welcome extends Component {
    constructor(props) {
        super(props);
        this.state = {
            username: ''
        }
    }

    componentWillMount() {
        let username = localStorage.getItem('username');
        this.setState({
            username: username
        })
    }

    render() {
        return (
            <div>welcome {this.state.username}</div>
        )
    }
}

export default Welcome;
import React, {Component} from 'react'

class Goodbye extends Component {
    constructor(props) {
        super(props);
        this.state = {
            username: ''
        }
    }

    componentWillMount() {
        let username = localStorage.getItem('username');
        this.setState({
            username: username
        })
    }

    render() {
        return (
            <div>goodbye {this.state.username}</div>
        )
    }
}

export default Goodbye;

我們可以經由歷程方才高階函數的思想來建立一个中心組件,也就是我們說的高階組件。

import React, {Component} from 'react'

export default (WrappedComponent) => {
    class NewComponent extends Component {
        constructor() {
            super();
            this.state = {
                username: ''
            }
        }

        componentWillMount() {
            let username = localStorage.getItem('username');
            this.setState({
                username: username
            })
        }

        render() {
            return <WrappedComponent username={this.state.username}/>
        }
    }

    return NewComponent
}
import React, {Component} from 'react';
import wrapWithUsername from 'wrapWithUsername';

class Welcome extends Component {

    render() {
        return (
            <div>welcome {this.props.username}</div>
        )
    }
}

Welcome = wrapWithUsername(Welcome);

export default Welcome;
import React, {Component} from 'react';
import wrapWithUsername from 'wrapWithUsername';

class Goodbye extends Component {

    render() {
        return (
            <div>goodbye {this.props.username}</div>
        )
    }
}

Goodbye = wrapWithUsername(Goodbye);

export default Goodbye;

看到沒有,高階組件就是把 username 經由歷程 props 通報給目的組件了。目的組件只管從 props內里拿來用就好了。

為了代碼的復用性,我們應當只管削減代碼的冗餘。

  1. 提取同享的state,假如有兩個組件都須要加載一樣的數據,那末他們會有雷同的 componentDidMount 函數。
  2. 找出反覆的代碼,每一個組件中constructor 和 componentDidMount都乾著一樣的事變,別的,在數據拉取時都邑顯現Loading… 案牘,那末我們應當思索怎樣運用高階組件來提取這些要領。
  3. 遷徙反覆的代碼到高階組件
  4. 包裹組件,而且運用props替代state
  5. 只管地簡化

組件開闢基本思想

單功用準繩

運用react時,組件或容器的代碼在基本上必需只擔任一塊UI功用。

讓組件堅持簡樸

  • 假如組件基本不須要狀況,那末就運用函數定義的無狀況組件。
  • 從機能上來講,函數定義的無狀況組件 > ES6 class 定義的組件 > 經由歷程 React.createClass() 定義的組件。
  • 僅通報組件所須要的屬性。只要當屬性列表太長時,才運用{...this.props}舉行通報。
  • 假如組件內里有太多的推斷邏輯(if-else語句)一般意味着這個組件須要被拆分紅更細的組件或模塊。
  • 運用邃曉的定名可以讓開闢者邃曉它的功用,有助於組件復用。

基本準則

  • shouldComponentUpdate中防止不必要的搜檢.
  • 只管運用不可變數據範例(Immutable).
  • 編寫針對產物環境的打包設置(Production Build).
  • 經由歷程Chrome Timeline來紀錄組件所消耗的資本.
  • componentWillMount或許componentDidMount內里經由歷程setTimeOut或許requestAnimationFram來耽誤實行那些須要大批盤算的使命.

組件開闢技能

form表單里的受控組件和不受控組件

受控組件

在大多數狀況下,我們引薦運用受控組件來完成表單。在受控組件中,表單數據由 React 組件擔任處置懲罰。下面是一個典範的受控組建。

class NameForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {value: ''};

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

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

  handleSubmit(event) {
    alert('A name was submitted: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          <input type="text" value={this.state.value} onChange={this.handleChange} />
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}

設置表單元素的value屬性以後,其顯現值將由this.state.value決議,以滿足React狀況的統一數據理念。每次鍵盤敲擊以後會實行handleChange要領以更新React狀況,顯現值也將跟着用戶的輸入轉變。

關於受控組件來講,每一次 state(狀況)變化都邑伴有相干聯的處置懲罰函數。這使得可以直接修正或考證用戶的輸入和提交表單。

不受控組件

由於不受控組件的數據泉源是 DOM 元素,當運用不受控組件時很輕易完成 React 代碼與非 React 代碼的集成。假如你願望的是疾速開闢、不請求代碼質量,不受控組件可以肯定程度上削減代碼量。不然。你應當運用受控組件。

平常狀況下不受控組件我們運用ref來獵取DOM元素舉行操縱。

class NameForm extends React.Component {
  constructor(props) {
    super(props);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleSubmit(event) {
    alert('A name was submitted: ' + this.input.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Name:
          <input type="text" ref={(input) => this.input = input} />
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}

組件前提推斷

三元函數組件推斷襯着

const sampleComponent = () => {
  return isTrue ? <p>True!</p> : <p>false!</p>
};

運用&&表達式替代不必要的三元函數

const sampleComponent = () => {
  return isTrue ? <p>True!</p> : <none/>
};
const sampleComponent = () => {
  return isTrue && <p>True!</p>
};

須要注重的是假如isTrue 為 0 ,實在會轉換成 false,然則在頁面中顯現的時刻,&&照樣會返回0顯現到頁面中。

多重嵌套推斷

// 題目代碼
const sampleComponent = () => {
  return (
    <div>
      {flag && flag2 && !flag3
        ? flag4
        ? <p>Blah</p>
        : flag5
        ? <p>Meh</p>
        : <p>Herp</p>
        : <p>Derp</p>
      }
    </div>
  )
};

處理計劃:

  • 最好計劃: 將邏輯移到子組件內部
  • 運用IIFE(Immediately-Invoked Function Expression 馬上實行函數)
  • 滿足前提的時刻運用return強迫跳出函數
const sampleComponent = () => {
  const basicCondition = flag && flag2 && !flag3;
  if (!basicCondition) return <p>Derp</p>;
  if (flag4) return <p>Blah</p>;
  if (flag5) return <p>Meh</p>;
  return <p>Herp</p>
}

setState異步性

在某些狀況下,React框架出於機能優化斟酌,可以會將屢次state更新兼并成一次更新。正由於如此,setState實際上是一個異步的函數。 假如在挪用setState()函數以後嘗試去接見this.state,你獲得的可以照樣setState()函數實行之前的效果。

然則,有一些行動也會阻撓React框架自身關於屢次state更新的兼并,從而讓state的更新變得同步化。 比方: eventListeners, Ajax, setTimeout 等等。

React框架之所以在挑選在挪用setState函數以後馬上更新state而不是採納框架默許的體式格局,即兼并屢次state更新為一次更新,是由於這些函數挪用(fetch,setTimeout等瀏覽器層面的API挪用)並不處於React框架的高低文中,React沒有方法對其舉行掌握。React在此時採納的戰略就是實時更新,確保在這些函數實行以後的其他代碼能拿到準確的數據(即更新過的state)。

處理setState函數異步的方法?

依據React官方文檔,setState函數實際上吸收兩個參數,个中第二個參數範例是一個函數,作為setState函數實行后的回調。經由歷程傳入回調函數的體式格局,React可以保證傳入的回調函數肯定是在setState勝利更新this.state以後再實行。

this.setState({count: 1}, () => {
    console.log(this.state.count); // 1
})

React源碼中setState的完成

ReactComponent.prototype.setState = function(partialState, callback) {
  invariant(
    typeof partialState === 'object' ||
    typeof partialState === 'function' ||
    partialState == null,
    'setState(...): takes an object of state variables to update or a ' +
    'function which returns an object of state variables.'
  );
  this.updater.enqueueSetState(this, partialState);
  if (callback) {
    this.updater.enqueueCallback(this, callback, 'setState');
  }
};

updater的這兩個要領,和React底層的Virtual Dom(假造DOM樹)的diff算法有嚴密的關聯,所以真正決議同步照樣異步的實際上是Virtual DOMdiff算法。

依靠注入

React中,想做依靠注入(Dependency Injection)實在相稱簡樸。可以經由歷程props來舉行通報。然則,假如組件數目許多,而且組件嵌套條理很深的話,這類體式格局就不太適宜。

高階組件

// inject.jsx
var title = 'React Dependency Injection';
export default function inject(Component) {
  return class Injector extends React.Component {
    render() {
      return (
        <Component
          {...this.state}
          {...this.props}
          title={ title }
        />
      )
    }
  };
}
// Title.jsx
export default function Title(props) {
  return <h1>{ props.title }</h1>;
}
// Header.jsx
import inject from './inject.jsx';
import Title from './Title.jsx';

var EnhancedTitle = inject(Title);
export default function Header() {
  return (
    <header>
      <EnhancedTitle />
    </header>
  );
}

context

React v16.3.0 之前的 Context:

var context = { title: 'React in patterns' };
class App extends React.Component {
  getChildContext() {
    return context;
  }
  // ...
}

App.childContextTypes = {
  title: PropTypes.string
};
class Inject extends React.Component {
  render() {
    var title = this.context.title;
  // ...
  }
}
Inject.contextTypes = {
  title: PropTypes.string
};

之前的 Context 作為一個試驗性子的 API,直到 React v16.3.0 版本前都一向不被官方所首倡去運用,其主要緣由就是由於在子組件中運用 Context 會損壞 React 運用的分型架構。

這裏的分形架構指的是從抱負的 React 運用的根組件樹中抽取的恣意一部分都還是一個可以直接運轉的子組件樹。在這個子組件樹之上再包一層,就可以將它無縫地移植到恣意一個其他的根組件樹中。

但假如根組件樹中有恣意一個組件運用了支撐透傳的 Context API,那末假如把包含了這個組件的子組件樹零丁拿出來,由於缺少了供應 Context 值的根組件樹,這時候的這個子組件樹是沒法直接運轉的。

而且他有一個致命缺點:任何一个中心通報的組件shouldComponentUpdate 函數返回false,組件都不會獲得更新。

新的Context Api

新的Context Api 採納聲明式的寫法,而且可以透過shouldComponentUpdate 函數返回false的組件繼承向下流傳,以保證目的組件肯定可以吸收到頂層組件 Context 值的更新,一舉處理了現有 Context API 的兩大弊病,也終究成為了 React 中的第一級(first-class) API

新的 Context API 分為三個構成部分:

  1. React.createContext 用於初始化一個 Context
  2. XXXContext.Provider作為頂層組件吸收一個名為 valueprop,可以吸收恣意須要被放入 Context 中的字符串,数字,以至是函數。
  3. XXXContext.Consumer作為目的組件可以出現在組件樹的恣意位置(在 Provider 以後),吸收 children prop,這裏的 children 必需是一個函數(context =&gt; ())用來吸收從頂層傳來的 Context
const ThemeContext = React.createContext('light');

class App extends React.Component {
  render() {
    return (
      <ThemeContext.Provider value="dark">
        <Toolbar />
      </ThemeContext.Provider>
    );
  }
}

function Toolbar(props) {
  return (
    <div>
      <ThemedButton />
    </div>
  );
}

function ThemedButton(props) {
  return (
    <ThemeContext.Consumer>
      {theme => <Button {...props} theme={theme} />}
    </ThemeContext.Consumer>
  );
}

事宜處置懲罰中的this指向題目

class Switcher extends React.Component {
  constructor(props) {
    super(props);
    this.state = { name: 'React in patterns' };
  }
  render() {
    return (
      <button onClick={ this._handleButtonClick }>
        click me
      </button>
    );
  }

  _handleButtonClick() {
    console.log(`Button is clicked inside ${ this.state.name }`);
    // 將致使
    // Uncaught TypeError: Cannot read property 'state' of null
  }
}

我們可以經由歷程下面三種體式格局簡樸完成this指向的綁定:

  • constructor 中事前綁定 this._buttonClick = this._handleButtonClick.bind(this);
  • 挪用時運用箭頭函數 <button onClick={ () => this._buttonClick() }>
  • ES7中的綁定操縱符 <button onClick={ ::this._buttonClick() }>

給setState傳入回調函數

setState() 不僅能接收一個對象,還能接收一個函數作為參數呢,該函數接收該組件前一刻的 state 以及當前的 props 作為參數,盤算和返回下一刻的 state。

// assuming this.state.count === 0
this.setState({count: this.state.count + 1});
this.setState({count: this.state.count + 1});
this.setState({count: this.state.count + 1});
// this.state.count === 1, not 3

this.setState((prevState, props) => ({
  count: prevState.count + props.increment
}));
// Passing object
this.setState({ expanded: !this.state.expanded });

// Passing function
this.setState(prevState => ({ expanded: !prevState.expanded }));

組件切換技能

import HomePage from './HomePage.jsx';
import AboutPage from './AboutPage.jsx';
import UserPage from './UserPage.jsx';
import FourOhFourPage from './FourOhFourPage.jsx';

const PAGES = {
  home: HomePage,
  about: AboutPage,
  user: UserPage
};

const Page = (props) => {
  const Handler = PAGES[props.page] || FourOhFourPage;

  return <Handler {...props} />
};

React style

組件分類

基本組件, 規劃組件, 排版組件

給無狀況的純UI組件運用款式

請堅持款式闊別那些離不開state的組件. 比方路由, 視圖, 容器, 表單, 規劃等等不該當有任何的款式或許css class出現在組件上. 相反, 這些龐雜的營業組件應當有一些帶有基本功用的無狀況UI組件構成.

class SampleComponent extends Component {
  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <Heading children='Sign In'/>
        <Input
          name='username'
          value={username}
          onChange={this.handleChange}/>
        <Input
          type='password'
          name='password'
          value={password}
          onChange={this.handleChange}/>
        <Button
          type='submit'
          children='Sign In'/>
      </form>
    )
  }
}

// 表達組件(帶款式)
const Button = ({
  ...props
  }) => {
  const sx = {
    fontFamily: 'inherit',
    fontSize: 'inherit',
    fontWeight: 'bold',
    textDecoration: 'none',
    display: 'inline-block',
    margin: 0,
    paddingTop: 8,
    paddingBottom: 8,
    paddingLeft: 16,
    paddingRight: 16,
    border: 0,
    color: 'white',
    backgroundColor: 'blue',
    WebkitAppearance: 'none',
    MozAppearance: 'none'
  }

  return (
    <button {...props} style={sx}/>
  )
}

款式模塊(style module)

平常來講, 在組件內寫死(hard code)款式應當是要被防止的. 這些有可以被差別的UI組件分享的款式應當被離開放入對應的模塊中.

// 款式模塊
export const white = '#fff';
export const black = '#111';
export const blue = '#07c';

export const colors = {
  white,
  black,
  blue
};

export const space = [
  0,
  8,
  16,
  32,
  64
];

const styles = {
  bold: 600,
  space,
  colors
};

export default styles
// button.jsx
import React from 'react'
import { bold, space, colors } from './styles'

const Button = ({
  ...props
  }) => {
  const sx = {
    fontFamily: 'inherit',
    fontSize: 'inherit',
    fontWeight: bold,
    textDecoration: 'none',
    display: 'inline-block',
    margin: 0,
    paddingTop: space[1],
    paddingBottom: space[1],
    paddingLeft: space[2],
    paddingRight: space[2],
    border: 0,
    color: colors.white,
    backgroundColor: colors.blue,
    WebkitAppearance: 'none',
    MozAppearance: 'none'
  };

  return (
    <button {...props} style={sx}/>
  )
};

款式函數(Style Functions)

// Modular powers of two scale
const scale = [
  0,
  8,
  16,
  32,
  64
];

// 經由歷程這個函數去獲得一部分的款式
const createScaledPropertyGetter = (scale) => (prop) => (x) => {
  return (typeof x === 'number' && typeof scale[x] === 'number')
    ? {[prop]: scale[x]}
    : null
};
const getScaledProperty = createScaledPropertyGetter(scale);

export const getMargin = getScaledProperty('margin');
export const getPadding = getScaledProperty('padding');
// 款式函數的用法
const Box = ({
  m,
  p,
  ...props
  }) => {
  const sx = {
    ...getMargin(m),
    ...getPadding(p)
  };

  return <div {...props} style={sx}/>
};

// 組件用法.
const Box = () => (
  <div>
    <Box m={2} p={3}>
      A box with 16px margin and 32px padding
    </Box>
  </div>
);

罕見小坑

state不更新?

class SampleComponent extends Component {
  // constructor function (or getInitialState)
  constructor(props) {
    super(props);
    this.state = {
      flag: false,
      inputVal: props.inputValue
    };
  }

  render() {
    return <div>{this.state.inputVal && <AnotherComponent/>}</div>
  }
}

如許做的風險在於, 有可以組件的props發作了轉變然則組件卻沒有被更新. 新的props的值不會被React認為是更新的數據由於組織器constructor或許getInitialState要領在組件建立以後不會再次被挪用了,因而組件的state不再會被更新。 要記着, State的初始化只會在組件第一次初始化的時刻發作。

class SampleComponent extends Component {
  // constructor function (or getInitialState)
  constructor(props) {
    super(props);
    this.state = {
      flag: false
    };
  }

  render() {
    return <div>{this.props.inputValue && <AnotherComponent/>}</div>
  }
}

更清潔的render函數?

更清潔的render函數? 這個觀點可以會有點讓人迷惑.

實在在這裏清潔是指我們在shouldComponentUpdate這個性命周期函數內里去做淺比較, 從而防止不必要的襯着.

class Table extends PureComponent {
  render() {
    return (
      <div>
        {this.props.items.map(i =>
          <Cell data={i} options={this.props.options || []}/>
        )}
      </div>
    );
  }
}

這類寫法的題目在於{this.props.options || []} 這類寫法會致使一切的Cell都被從新襯着縱然只要一個cell發作了轉變. 為何會發作這類事呢?

仔細觀察你會發明, options這個數組被傳到了Cell這個組件上, 平常狀況下, 這不會致使什麼題目. 由於假如有其他的Cell組件, 組件會在有props發作轉變的時刻淺對照props而且跳過襯着(由於關於其他Cell組件, props並沒有發作轉變). 然則在這個例子內里, 當optionsnull時, 一個默許的空數組就會被當做Props傳到組件內里去. 事實上每次傳入的[]都相稱於建立了新的Array實例. 在JavaScript內里, 差別的實例是有差別的實體的, 所以淺比較在這類狀況下總是會返回false, 然後組件就會被從新襯着. 由於兩個實體不是統一個實體. 這就完整損壞了React關於我們組件襯着的優化.

const defaultval = [];  // <---  也可以運用defaultProps
class Table extends PureComponent {
  render() {
    return (
      <div>
        {this.props.items.map(i =>
          <Cell data={i} options={this.props.options || defaultval}/>
        )}
      </div>
    );
  }
}

照樣屢次從新襯着

class App extends PureComponent {
  render() {
    return <MyInput
      onChange={e => this.props.update(e.target.value)}/>;
  }
}
class App extends PureComponent {
  update(e) {
    this.props.update(e.target.value);
  }

  render() {
    return <MyInput onChange={this.update.bind(this)}/>;
  }
}

在上面的兩個壞實踐中, 每次我們都邑去建立一個新的函數實體. 和第一個例子相似, 新的函數實體味讓我們的淺比較返回false, 致使組件被從新襯着. 所以我們須要在更早的時刻去bind我們的函數.

class App extends PureComponent {
  constructor(props) {
    super(props);
    this.update = this.update.bind(this);
  }

  update(e) {
    this.props.update(e.target.value);
  }

  render() {
    return <MyInput onChange={this.update}/>;
  }
}

定名

援用定名

React模塊名運用帕斯卡定名,實例運用駱駝式定名

// bad
import reservationCard from './ReservationCard';

// good
import ReservationCard from './ReservationCard';

// bad
const ReservationItem = <ReservationCard />;

// good
const reservationItem = <ReservationCard />;

高階模塊定名

// bad
export default function withFoo(WrappedComponent) {
  return function WithFoo(props) {
    return <WrappedComponent {...props} foo />;
  }
}

// good
export default function withFoo(WrappedComponent) {
  function WithFoo(props) {
    return <WrappedComponent {...props} foo />;
  }

  const wrappedComponentName = WrappedComponent.displayName
    || WrappedComponent.name
    || 'Component';

  WithFoo.displayName = `withFoo(${wrappedComponentName})`;
  return WithFoo;
}

屬性定名

防止運用DOM相干的屬性來用作其他的用處。

// bad
<MyComponent style="fancy" />

// good
<MyComponent variant="fancy" />

私有函數增加 _ 前綴?

在React模塊中,不要給所謂的私有函數增加 _ 前綴,本質上它並非私有的。

為何?_ 下劃線前綴在某些言語中一般被用來示意私有變量或許函數。然則不像其他的一些言語,在JS中沒有原生支撐所謂的私有變量,一切的變量函數都是共有的。只管你的企圖是使它私有化,在之前加高低劃線並不會使這些變量私有化,而且一切的屬性(包含有下劃線前綴及沒有前綴的)都應當被視為是共有的。

Ordering React 模塊性命周期

class extends React.Component 的性命周期函數:
可選的 static 要領

  • constructor 組織函數
  • getChildContext 獵取子元素內容
  • componentWillMount 模塊襯着前
  • componentDidMount 模塊襯着后
  • componentWillReceiveProps 模塊將接收新的數據
  • shouldComponentUpdate 推斷模塊需不須要從新襯着
  • componentWillUpdate 上面的要領返回 true, 模塊將從新襯着
  • componentDidUpdate 模塊襯着完畢
  • componentWillUnmount 模塊將從DOM中消滅, 做一些清算使命

點擊回調或許事宜處置懲罰器 如 onClickSubmit()onChangeDescription()

render 里的 getter 要領 如 getSelectReason()getFooterContent()

可選的 render 要領 如 renderNavigation()renderProfilePicture()

render render() 要領

怎樣定義 propTypes, defaultProps, contextTypes, 等等其他屬性…

import React from 'react';
import PropTypes from 'prop-types';

const propTypes = {
  id: PropTypes.number.isRequired,
  url: PropTypes.string.isRequired,
  text: PropTypes.string,
};

const defaultProps = {
  text: 'Hello World',
};

class Link extends React.Component {
  static methodsAreOk() {
    return true;
  }

  render() {
    return <a href={this.props.url} data-id={this.props.id}>{this.props.text}</a>;
  }
}

Link.propTypes = propTypes;
Link.defaultProps = defaultProps;

export default Link;
    原文作者:樓蘭小騎士
    原文地址: https://segmentfault.com/a/1190000015007317
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞