React文档笔记day04

state属性和声明周期

到目前为止我们了解到一种方法改变UI界面ReactDOM.render(),改变渲染的输出。
看看一看之前的时钟例子

function tick() {
  const element = (
    

Hello, world!

It is {new Date().toLocaleTimeString()}.

); ReactDOM.render( element, document.getElementById('root') ); } setInterval(tick, 1000);

在这一篇里面我们将会学习怎么真正的封装一个时钟,并且时钟还会自动的更新时间。

这个过程比较的复杂,可以先从封装时钟外观开始

function Clock(props) {
  return (
    

Hello, world!

It is {props.date.toLocaleTimeString()}.

); } function tick() { ReactDOM.render( , document.getElementById('root') ); } setInterval(tick, 1000);

现在外观的样子是


React文档笔记day04_第1张图片
image.png

我们现在想让之中自己去开启定时器,以后再用的时候直接创建一个时钟,他就可以自己运行起来,不用再最后我们添加一个定时器,每一秒调用一次tick函数。就想这样

ReactDOM.render(
  ,
  document.getElementById('root')
);

开始动手吧
为了实现上面的效果,我们需要给组件添加一个属性statestate属性和props相似,不同的是state是一个私有属性,并且完全由组件控制

我们之前有提到过函数型的组件和类型组件相似,但是类型组件比函数型组件对一些功能,这里state就是这个多出来的功能。state只能在类型的组件中使用。

把函数型组件转化成类型组件

  • 创建一个ES6的类,继承自React.Component
  • 在类中添加一个空函数render()
  • 把韩属性的组件中,函数体内容符合复制到render()里面
  • 把render()里面的props改成this.props
  • 删除函数型组件的声明

最终效果

class Clock extends React.Component {
  render() {
    return (
      

Hello, world!

It is {this.props.date.toLocaleTimeString()}.

); } }

现在我们就创建了一个时钟类,因为是类,所以我们可以给这个时钟里面添加state属性和声明周期钩子(lifecycle hooks)

给类添加state属性

我们需要把date属性从props移动到state中,需要三步

  • 1 在render()方法中把this.props.date替换成this.state.date
class Clock extends React.Component {
  render() {
    return (
      

Hello, world!

It is {this.state.date.toLocaleTimeString()}.

); } }
  • 2 在类中添加一个构造函数constructor构造函数顾名思义就是在创建各个累的时候自动调用的函数。在构造函数中添加一个属性state,并且附一个初始值。
class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  render() {
    return (
      

Hello, world!

It is {this.state.date.toLocaleTimeString()}.

); } }

我们把props传进构造函数,并且类型的组件总是传递props到基本的构造函数,然后调用。

constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

在构造函数中一定要先调用父类的构造函数

  • 3 移除date属性从元素
ReactDOM.render(
  ,
  document.getElementById('root')
);

现在来看看整体效果

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  render() {
    return (
      

Hello, world!

It is {this.state.date.toLocaleTimeString()}.

); } } ReactDOM.render( , document.getElementById('root') );

后面我们将会来把定时器添加到类里面。

添加声明周期方法到类中

一个应用功能程序中有很多的组件,那么当组件被销毁的时候内存的释放就会非常重要。

先在需要在组件第一次渲染到UI界面上的时候开启一个定时器。需要在组件被销毁的时候清除定时器。这就需要用到生命周期的钩子函数

我们可以在类型组件中声明一些特殊的方法,这些方法会在组件被加载和被销毁的时候自动调用。

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  componentDidMount() {

  }

  componentWillUnmount() {

  }

  render() {
    return (
      

Hello, world!

It is {this.state.date.toLocaleTimeString()}.

); } }

上面代码中componentDidMount()方法会在组件被加载到Dom节点的时候被调用。我们可以再这里开启定时器

componentDidMount() {
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }

请注意我们在这里把timerID存进了this中
虽然props是有react自己创建,state有特殊的用处,我们还可以在类中自由的添加其他的属性不用于视觉输出。
如果某个属性没有出现在render()函数中,那么也就没有必要出现在state中。
我们需要在componentWillUnmount()中销毁定时器

componentWillUnmount() {
    clearInterval(this.timerID);
  }

最后我们写一个trick函数,这个函数将会每一秒调用一次。
并且使用this.setState()方法安排主键的更新

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  componentDidMount() {
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }

  componentWillUnmount() {
    clearInterval(this.timerID);
  }

  tick() {
    this.setState({
      date: new Date()
    });
  }

  render() {
    return (
      

Hello, world!

It is {this.state.date.toLocaleTimeString()}.

); } } ReactDOM.render( , document.getElementById('root') );

总体效果

正确的是使用state

错误的使用方式

// Wrong
this.state.comment = 'Hello';

正确的使用方式

// Correct
this.setState({comment: 'Hello'});

唯一的一个使用this.state的地方是在构造方法里面。

state的更新可能是异步至此那个的

this.props和this.state可能是异步更新的,所以不能根据这两个属性计算新的值,例如

这是错的

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

正确的使用方式
把上一个状态值传进来

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

state更新是合并更新的

当调用setState()的时候,React会合并所有提供的变量到当前state中
例如state中会包含几个独立的变量

constructor(props) {
    super(props);
    this.state = {
      posts: [],
      comments: []
    };
  }

然后你可以分别更新这些属性

componentDidMount() {
    fetchPosts().then(response => {
      this.setState({
        posts: response.posts
      });
    });

    fetchComments().then(response => {
      this.setState({
        comments: response.comments
      });
    });
  }

this.setState({comments})保留this.state.posts原封不动,但是this.state.comments会被替换

数据详向下传递

为什么state被称为本地的或者封装的数据,除了拥有和设置他的组件,其他的组件都不可能访问得到state

组件可以将state作为属性向下传递到子组件中

It is {this.state.date.toLocaleTimeString()}.

这的操作也可以用在用户自定义的组件中


FormattedDate组件可以接受date到自己的props中但是他不知道这个date到底来自于组件clock的state还是props或是手动输入的。

这样的数据传递称之为单向数据传递
任何state都会被一个组件拥有,任何被和这个state驱动的UI组件只有可能向下影响。
所有组件之间是独立互不影响的

function App() {
  return (
    
); } ReactDOM.render( , document.getElementById('root') );
React文档笔记day04_第2张图片
image.png

每一个时钟都建立自己的timer,并且独立更新

你可能感兴趣的:(React文档笔记day04)