React填坑笔记

组件的生命周期

图例

res/react_lifecircle.png
res/ajs-life.png

实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import React from 'react';
class Card extends React.Component {
/*
- 组件的生命周期
*/
constructor (props, context) {
super(props, context);
this.state = {text: 'Hello world'};
}
componentWillMount() {
}
// render
componentDidMount() {
}
componentWillReceiveProps(nextProps) {
}
shouldComponentUpdate(nextProps, nextState) {
return nextState.text != this.state.text;
}
componentWillUpdate (nextProps, nextState) {
}
// render
componentDidUpdate() {
}
componentWillUnmount() {
}
render() {
return (
<div>
{this.state.text}
</div>
);
}
}
export default Card

mounting

  • constructor()
  • componentWillMount()
  • render()
  • componentDidMount()

Updating

  • componentWillReceiveProps()
  • shouldComponentUpdate()
  • componentWillUpdate()
  • componentDidUpdate()
  • render()
  • componentDidUpdate()

Unmounting

  • componentWillUnmount()

调用forceUpdate时

  • componentWillUpdate()
  • render()
  • componentDidUpdate()

接收到新参数时props

  • 同Updating

setState时

  • shouldComponentUpdate()
  • componentWillUpdate()
  • componentDidUpdate()
  • render()
  • componentDidUpdate()

React的 props & state

  • props

    props (properties的简称) 是一个组件的 配置选项 。props是由上到下指定且不可改变的。一个组件不能改变自身的props, 但要负责设置子组件的props

  • state

    当组件加载时,state有一个默认值,后来state会不定期地改变(主要是用户行为触发的)。state是每一时间点组件状态的代表-快照。
    一个组件在内部管理自己的state,除了设置子组件的state之外,该组件与其子组件的state没有任何联系。你可以认为state是私有的。

  • 改变 props & state

    | - | props | state |
    |: — |: — :|: — :|
    | 能否从父组件获取初始值? | Yes | Yes |
    | 能否被父组件改变? | Yes | No |
    | 能否在组件内设置默认值?* | Yes | Yes |
    | 能否在组件内改变? | No | Yes |
    | 能否设置子组件的初始值? | Yes | Yes |
    | 能否在子组件中改变? | Yes | No |

  • 组件的类型与 props & state 关系

    • 组件是否应该有state?

      state是可选项,不是React强制实现的。因为state增加了组件的复杂度同时降低了组件的可预见性,所以没有state的组件 要略胜一筹。即便在一个交互式应用中,你显然离不开state,你也要避免有太多的有状态化组件(含有state的组件)。

    • 组件类型:

      • 无状态组件

        — 只有props, 没有state。 除去render() 函数和所有围绕props的逻辑之外,没有什么要关心的地方。这使他们易于理解且易于测试

      • 有状态组件

        既有props又有state。也被称作状态管理者 。他们负责客户端-服务器通信(通过XHR, web sockets, 等),数据处理和给用户行为反馈。

    • 哪些组件应该有 State?

      大部分组件的工作应该是从 props 里取数据并渲染出来。但是,有时需要对用户输入、服务器请求或者时间变化等作出响应,这时才需要使用 state

      尝试把尽可能多的组件无状态化。 这样做能隔离state,把它放到最合理的地方,也能减少冗余,同时易于解释程序运作过程。

      常用的模式是创建多个只负责渲染数据的无状态(stateless)组件,在它们的上层创建一个有状态(stateful)组件并把它的状态通过props传给子级。这个有状态的组件封装了所有用户的交互逻辑,而这些无状态组件则负责声明式地渲染数据。

    • 哪些应该作为State

      State应该包括那些可能被组件的事件处理器改变并触发用户界面更新的数据。真实的应用中这种数据一般都很小且能被 JSON 序列化。当创建一个状态化的组件时,想象一下表示它的状态最少需要哪些数据,并只把这些数据存入this.state。在render()里再根据state来计算你需要的其它数据。你会发现以这种方式思考和开发程序最终往往是正确的,因为如果在state里添加冗余数据或计算所得数据,需要你经常手动保持数据同步,不能让 React 来帮你处理。

    • 哪些不应该作为 State?

      this.state 应该仅包括能表示用户界面状态所需的最少数据。因此,它不应该包括:

      • 计算所得数据: 不要担心根据 state 来预先计算数据 —— 把所有的计算都放到 render() 里更容易保证用户界面和数据的一致性。例如,在 state 里有一个数组(listItems),我们要把数组长度渲染成字符串, 直接在 render() 里使用 this.state.listItems.length + ’ list items’ 比把它放到 state 里好的多。

      • React 组件: 在 render() 里使用当前 propsstate 来创建它。

      • 基于 props 的重复数据: 尽可能使用 props 来作为惟一数据来源。把 props 保存到 state 的一个有效的场景是需要知道它以前值的时候,因为未来的 props 可能会变化。

    • 判断数据是否可以作为 state ,简单地对每一项数据提出三个问题:

      • 是否是从父级通过 props 传入的?如果是,可能不是 state

      • 是否会随着时间改变?如果不是,可能不是 state

      • 能根据组件中其它 state 数据或者 props 计算出来吗?如果是,就不是 state

React最佳实践

一些细节

  • 组件绑定 this
1
2
3
4
5
6
7
8
9
10
11
//Constructor 内绑定
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
//Class 属性中使用 = 和箭头函数
handleChange = () => {
// call this function from render
// and this.whatever in here works fine.
};
  • setState 接受一个函数作为参数(setState的异步性)
1
this.setState(prevState => ({ expanded: !prevState.expanded }))
  • 避免以下情况

因为每次父组件 render 的时候,都会新建一个新的函数并传递给 input,如果 input 是一个 React 组件,这会粗暴地直接导致这个组件的 re-render(props参数改变)

1
2
3
4
5
6
7
8
9
//不要:
onChange = {(e) => { model.name = e.target.value }}
//而是:
onChange = {this.handleChange}
handleChange = (e) => {
this.props.model.name = e.target.value
}

React 组件设计

  1. 分解组件,切割子组件

  2. 模板化组件,利用React 可以传递 React element 的特性,我们将 React element 进行组件间传递

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Comment extends React.Component {
render() {
const metadata = this.props.publishTime ?
<PublishTime time={this.props.publishTime} /> :
<span>Saving...</span>;
const actions = [];
if (this.props.isSignedIn) {
actions.push(<LikeAction />);
actions.push(<ReplyAction />);
}
if (this.props.isAuthor) {
actions.push(<DeleteAction />);
}
return <CommentTemplate metadata={metadata} actions={actions} />;
}
  1. 高阶组件

withLinkAnalytics 函数并不会去改变 WrappedComponent 组件本身,更不会去改变 WrappedComponent 组件的行为。而是返回了一个被包裹的新组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//场景:我们想统计页面中所有链接的点击信息。在链接点击时,发送统计请求,同时这条请求需要包含此页面 document 的 id 值
function withLinkAnalytics(mapPropsToData, WrappedComponent) {
class LinkAnalyticsWrapper extends React.Component {
componentDidMount() {
ReactDOM.findDOMNode(this).addEventListener('click', this.onClick);
}
componentWillUnmount() {
ReactDOM.findDOMNode(this).removeEventListener('click', this.onClick);
}
onClick = (e) => {
if (e.target.tagName === 'A') { // Naive check for <a> elements
const data = mapPropsToData ? mapPropsToData(this.props) : {};
sendAnalytics('link clicked', data);
}
};
render() {
// Simply render the WrappedComponent with all props
return <WrappedComponent {...this.props} />;
}
}
class Document extends React.Component {
render() {
// ...
}
}
export default withLinkAnalytics((props) => ({
documentId: props.documentId
}), Document);