React_2

时间:April 30, 2021 分类:

目录:

组件和prop

参考react.docschina.org

定义组件

定义组件最简单的方式就是编写JavaScript函数

function HelloMessage(props) {
    return <h1>Hello World!</h1>;
}

const element = <HelloMessage />;

ReactDOM.render(
    element,
    document.getElementById('example')
);

这里HelloMessage方法就是一个有效的React接收带有数据的props对象并返回一个React

当然也可以通过ES6的class来定义组件

class HelloMessage extends React.Component {
  render() {
    return <h1>Hello World!</h1>;
  }
}

原生HTML元素名以小写字母开头,而自定义的React类名以大写字母开头,除此之外还需要注意组件类只能包含一个顶层标签,否则会报错。

如果需要向组件传递参数,就需要使用props传递参数

function HelloMessage(props) {
    return <h1>Hello {props.name}!</h1>;
}

const element = <HelloMessage name="why"/>;

ReactDOM.render(
    element,
    document.getElementById('example')
);

整体渲染流程:

  1. 调用ReactDOM.render()函数,并传入element
  2. React调用HelloMessage组件,并将{name: 'why'}作为props传入
  3. HelloMessage组件将<h1>Hello why!</h1>元素作为返回值
  4. ReactDOM将DOM更新为<h1>Hello why!</h1>

复合组件

组件可以在输出中引用其他组件

function Name(props) {
    return <h1>网站名称:{props.name}</h1>;
}
function Url(props) {
    return <h1>网站地址:{props.url}</h1>;
}
function App() {
    return (
    <div>
        <Name name="why's blog" />
        <Url url="http://www.whysdomain.com" />
    </div>
    );
}

ReactDOM.render(
     <App />,
    document.getElementById('example')
);

提取组件

示例一个组件

function Comment(props) {
  return (
    <div className="Comment">
      <div className="UserInfo">
        <img className="Avatar"
          src={props.author.avatarUrl}
          alt={props.author.name}
        />
        <div className="UserInfo-name">
          {props.author.name}
        </div>
      </div>
      <div className="Comment-text">
        {props.text}
      </div>
      <div className="Comment-date">
        {formatDate(props.date)}
      </div>
    </div>
  );
}

这里接收author对象、text和data,可以提取一个Avatar组件

function Avatar(props) {
  return (
    <img className="Avatar"
      src={props.user.avatarUrl}
      alt={props.user.name}
    />
  );
}

这里使用的user,和外边传递的author不一样,就需要进行一下赋值

function Comment(props) {
  return (
    <div className="Comment">
      <div className="UserInfo">
        <Avatar user={props.author} />
        <div className="UserInfo-name">
          {props.author.name}
        </div>
      </div>
      <div className="Comment-text">
        {props.text}
      </div>
      <div className="Comment-date">
        {formatDate(props.date)}
      </div>
    </div>
  );
}

props只读性

组件无论是使用函数声明还是通过class声明,都决不能修改自身的props

State和生命周期

通过定时器方式

之前时钟例子

function tick() {
  const element = (
    <div>
      <h1>Hello, world!</h1>
      <h2>It is {new Date().toLocaleTimeString()}.</h2>
    </div>
  );
  ReactDOM.render(
    element,
    document.getElementById('root')
  );
}

setInterval(tick, 1000);

可以先将时钟变为Clock组件,还是设置计时器每秒更新一次

function Clock(props) {
  return (
    <div>
      <h1>Hello, world!</h1>
      <h2>It is {props.date.toLocaleTimeString()}.</h2>
    </div>
  );
}

function tick() {
  ReactDOM.render(
    <Clock date={new Date()} />,
    document.getElementById('root')
  );
}

setInterval(tick, 1000);

但是还是需要外部的计时器,想实现的功能为

ReactDOM.render(
  <Clock />,
  document.getElementById('root')
);

就需要state,与prop不通的是,state是私有的

state使用

class Clock extends React.Component {
  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.props.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

向组件中添加state

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

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

声明周期使用

然后再在生命周期上进行数据更新

  • 当组件第一次被渲染到DOM的时候,就可以设置计时器,这个过程为mount,对应方法componentDidMount
  • 当组件被删除的时候的操作,为umount,对应方法为componentWillUnmount

可以在渲染的时候,在class中添加定时器的数据流

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

定时器调用的方法为

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

在删除的时候清除

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

整体就是

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 (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

ReactDOM.render(
  <Clock />,
  document.getElementById('root')
);

整体流程为

  1. <Clock />被传递给ReactDOM.render()的时候,React会调用Clock组件的构造函数
  2. React根据render()转为React的DOM
  3. DOM插入到浏览器上的时候,会调用ComponentDidMount()的生命周期方法,会向浏览器请求设置一个计时器来调用tick方法
  4. 浏览器每秒调用一次tick()方法,通过setState()更新数据,React发现数据变化后会重新确定DOM进行渲染输出
  5. 一旦Clock组件从DOM移除,就会调用componentWillUnmount()停止设置的计时器

state触发react更新的情况

首先

  • this.state.comment = 'Hello';只会进行赋值,不会触发渲染组件
  • this.setState({comment: 'Hello'});会触发渲染组件
this.setState({
  counter: this.state.counter + this.props.increment,
});

以上可能无法更新计数器,因为this.props和this.state可能会异步更新,解决方式是接收一个函数而不是对象

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

this.setState(function(state, props) {
  return {
    counter: state.counter + props.increment
  };
});

数据是自上向下流动

function FormattedDate(props) {
  return <h2>现在是 {props.date.toLocaleTimeString()}.</h2>;
}

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 (
      <div>
        <h1>Hello, world!</h1>
        <FormattedDate date={this.state.date} />
      </div>
    );
  }
}

ReactDOM.render(
  <Clock />,
  document.getElementById('example')
);