props vs state

组件化开发思想第二条是:

  1. 样式跟状态分离。

现在我们来实现一个时钟应用:只是显示当前时间!

显示

用stateless组件,so easy:

const Clock = (props) => {
  return <h1>{new Date().toLocaleTimeString()}</h1>
}

state

stateless组件和props都是静态的,只能显示出固定的内容, 如果我们要显示动态内容(每一秒的时间都不一样呢),需要用到state。so,我们转化为class组件。

class Clock2 extends React.Component {
  constructor(props) {
    super(props);// constructor里第一行必须是这行,否则会报错
    this.state = {
      time: new Date().toLocaleTimeString(), // 这里是唯一一处直接复制给this.state的,其他地方请用this.setState({...})
    }
  }
  render() {
    return <h1>{this.state.time}</h1>
  }
}

添加定时器

然而上述代码还是静态的,只能显示constructor执行那一秒时的时间,我们需要用setInterval起一个定时器,每秒去更新时间。

在哪里起定时器呢? 应该是在DOM节点被渲染的时候, 这个叫Mounting。 同样,在不需要Clock组件的时候,我们也需要清除定时器,否则会出现内存泄露。那应该在哪里清除呢?应该在DOM节点被卸载的时候, 叫Unmounting

React提供了这两个时间点的回调函数给我们用。

class Clock2 extends React.Component {
  constructor(props) {
    super(props);// constructor里第一行必须是这行,否则会报错
    this.state = {
      time: new Date().toLocaleTimeString(), // 这里是唯一一处直接复制给this.state的,其他地方请用this.setState({...})
    }
  }
  componentDidMount() {
    console.log('componentDidMount');
    // timer不需要拿来渲染, 所以不要把timer让在this.state里
    this.timer = setInterval(() => {
      this.setState({
        time: new Date().toLocaleTimeString(),
      }) // 注意,如果直接修改this.state.time = new Date().toLocaleTimeString()是不行的, 不会渲染页面!
    })
  }
  componentWillUnmount() {
    console.log('componentDidMount');
    clearInterval(this.timer);
  }
  render() {
    return <h1>{this.state.time}</h1>
  }
}

一定要注意代码里面的几条注释!

重构:显示和状态分离

我们之前说了,用stateless组件来负责显示,class组件来负责状态和逻辑,代码可以改为:

import React from 'react';

const Clock = (props) => {
  return <h1>{props.time}</h1>
}

class ClockContainer extends React.Component {
  constructor(props) {
    super(props);// constructor里第一行必须是这行,否则会报错
    this.state = {
      time: new Date().toLocaleTimeString(), // 这里是唯一一处直接复制给this.state的,其他地方请用this.setState({...})
    }
  }
  componentDidMount() {
    console.log('componentDidMount');
    // timer不需要拿来渲染, 所以不要把timer让在this.state里
    this.timer = setInterval(() => {
      this.setState({
        time: new Date().toLocaleTimeString(),
      }) // 注意,如果直接修改this.state.time = new Date().toLocaleTimeString()是不行的, 不会渲染页面!
    })
  }
  componentWillUnmount() {
    console.log('componentDidMount');
    clearInterval(this.timer);
  }
  render() {
    return <Clock time={this.state.time} />
  }
}
export default ClockContainer;

这样我们就把负责静态内容显示的组件拆为Clock,而用ClockContainer专门负责逻辑:每秒更新一次状态。

肯定会有人说,这完全就是多此一举把代码搞复杂了啊。 是的!在这个简单的例子里面确实没必要,但是如果你是在实战中,可能Clock这个负责显示的组件会很复杂,有颜色,有布局,有不同的形状吗,甚至可能还会调用很多其他更小的组件,这样拆分后你在修改Clock的时候就完全不用管ClockContainer。 同样,如果有一天你想把显示逻辑改为精确到毫秒,你也只用修改ClockContainer而完全不用动Clock。

当然,如果你的实际项目就是像demo这么简单的话, 我觉得你应该使用jQuery甚至直接用原生js而不用React的啊! I really mean it!

results matching ""

    No results matching ""