寫 React Components 該注意的6個地方與技巧

在寫 React 的過程中,發現有一些小地方是我們可以多了解、多注意的,因此記錄與分享。

一、SetState 傳遞 Function

在 React 官方文件直接寫到,State 的更新是非同步的,通常會 batch 更新,因此直接依賴之前的 state 來更新 State 可能會出錯。

不好的寫法:

// Wrong
this.setState({
counter: this.state.counter + this.props.increment,
});

較好的寫法:

// Correct
this.setState((prevState, props) => ({
counter: prevState.counter + props.increment
}));

二、使用 PureComponent

在適當的時機下,透過 PureComponent 可以提升效能,這是由於繼承 React.PureComponent 在shouldComponentUpdate預設實作 shadow compare 新的 props & state 與舊的 props & state,如果兩者相同就會回傳 false,不 re-render component。

class Button extends React.PureComponent {
constructor(props) {
super(props);
this.state = {count: 1};
}

render() {
return (
<button
color={this.props.color}
onClick={() => this.setState(state => ({count: state.count + 1}))}>
Count: {this.state.count}
</button>
);
}
}

三、撰寫 Pure Render Function

Pure render 是指 render function 是 pure function,以及相同的 Input 應該產生相同的 output。

而在 React 中我們有時候容易寫出不是 pure render 的 function,這會造成 PureComponent shouldComponentUpdate 的結果都會是 ture,例如:

// onClick 每次產生新的 function
<a href="#" onClick={() => send(user.id)}> 
Click Me
</a>

// style 每次都會產生新的物件
<div style={{ color: 'red' }} /> 
// filterItems每次都會產生新的物件
render() {
  const filterItems = this.props.items.filter(...)
  return (
{
filterItems.map(() => ...
    }
  )
}

這些可以透過預先將變數寫在 class 外面,或是 class 的 this,或是在 componentWillReceiveProps setSstate 避免。

四、使用 class properties 讓寫法更乾淨

class properties 是新的語法,目前還在提案的階段,但已經可以透過 Babel class-properties-transform 來使用讓我們少寫許多相同的程式碼。

好處是過往 event bind this 我們可以這樣寫:

// bad: 每次 bind 會產生新的 function
<a href="#" onClick={handleClick.bind(this)}> 
Click me
</a>

也可以使用下面這種寫法:

// bad: 每次都需要多寫 bind 
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};

// This binding is necessary to make `this` work in the callback
this.handleClick = this.handleClick.bind(this);
}
  handleClick() {
// handle click
}
}

但現在我們可以直接這樣寫:

// good
class Toggle extends React.Component {
state = {isToggleOn: true};

handleClick = () => {
// handle click
}
}

而 Prop type 的檢查也可以直接寫法在裡面

class Toggle extends React.Component {
....
  static propTypes = {
title: string
}

static defaultProps = {
title: 'Name'
}
...
}

五、Function vs const

我們可以直接透過 Function 定義 component

function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}

而在 ES6 的寫法下,也可以使用 arrow function 簡化:

const Welcome = (props) => ((
<h1>Hello, {props.name}</h1>;
))

然而,雖然寫法上更為簡潔,但因為是匿名函數,會造成在 debug 的時候,Function 的名稱會難以辨識: <anonymous>。

六、Render Props & Hight Order Component 技巧

當我們需要更加地 Reuse Component,並讓所有 Component 擁有最少的相同的程式碼,可以透過 Render Props 或是 HOC 的技巧,簡化 component

Render Props:

The term “render prop” refers to a simple technique for sharing code between React components using a prop whose value is a function.
<DataProvider render={data => (
<h1>Hello {data.target}</h1>
)}/>

Hight Order Component

Concretely, a higher-order component is a function that takes a component and returns a new component.

結論

雖然標題是「最佳實踐」,但我相信最後這些寫法都是一種權衡,有時候是為了讓我們可以寫的更快,有時候是為了效能,而有時候是為了可讀性,而這些或許或根據不同的專案,不同情況而決定,但最重要的或許是先了解它們的優缺點,才能知道怎麼運用。