React学习--组件、props、state、生命周期

1、定义组件

    通过函数定义组件:js函数可以接收一个props属性,并返回一个React组件

function MsgBox(props){
  return 
hello {props.name}
}

    通过类继承定义组件:通过继承React.Component产生一个类组件,通过this.props访问类属性,并在render函数中返回react组件。

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

Hello {this.props.name}

} }

   注意:组件名称必须以大写字母开头。例如

表示一个DOM标签,但表示一个组件,并且在使用该组件前必须定义或引入它。

2、渲染组件

    通过ReactDOM.render()函数将组件MsgBox渲染到页面

ReactDOM.render(
  ,                            //要渲染的组件
  document.getElementById('app'),        //组件渲染的目标位置
  ()=>{                                  //渲染结束后的回调函数
    console.log("render回调");
  }
)

在渲染前,React DOM 首先会比较元素内容先后的不同,而在渲染过程中只会更新改变了的部分。

    条件渲染:可以为组件根据不同条件定义不同的返回值,实现条件渲染,或者可以将不同的返回值先储存在元素变量中,最后再统一与其他组件部分一起返回。例如根据用户的登录状态定义不同的提示语保存在变量prompt中,最后将变量prompt与标签button放在一个div中一起return,当用户点击button切换登录状态时,显示不同的提示语。

class Login extends React.Component{
  constructor(props){
    super(props);
    this.state={
      hasLogin:true
    };
    this.shiftLog=this.shiftLog.bind(this);
  }

  shiftLog(){
    this.setState({
      hasLogin:!this.state.hasLogin
    })
  }

  render(){
    let prompt;
    if(this.state.hasLogin){     //根据hasLogin的不同值,设置变量prompt的值
      prompt= 

欢迎你!

}else{ prompt=

请登陆!

} return(
{prompt} //渲染变量prompt
) } }

    阻止渲染:如果不希望一个组件进行渲染的话,可以让组件返回null,例如当hide为true时,不渲染组件Warning:

function Warning(props) {
  if(props.hide){
    return null;      //不渲染组件Warning
  }

  return 

show warning

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

3、组合组件

    我们可以定义子组件,然后在父组件中使用子组件,注意最后返回只能有一个根div标签。

//子组件
function Child(props) {
  return 

这是{props.name}

; } //父组件 function Parent() { return (
//返回一个根标签
) } ReactDOM.render( , document.getElementById('app') } )

    渲染结果:

    

    提取组件:相反的思路,当我们将一个传统的页面组件化为一个react页面时,我们可以将其中重复使用的部分抽象为一个组件,使得页面更为简洁、易于复用。在子组件的基础上,还可以将其外围容器抽象为父组件,层层嵌套,简化开发。

    组件容器:有时候我们的组件作为一个容器,并不知道要使用什么子组件,这时我们可以使用props.children来暂时代替子组件,当子组件插入时再替换到那个位置

function Parent(props) {
  return (
    
父组件 {props.children} //将传入不同的children属性渲染在此位置
) } function Child() { return
子组件
} function Composition() { return( }/> //通过属性的方式传递模块渲染到父容器中 ) }

    如果父组件容器有多个插槽,我们想把子组件放到指定的插槽中,这时就应该自定义属性名来指定插槽的名字。例如父组件容器有两个插槽,一个left,一个right,我们希望把组件放入left,把组件放入right:

function Left() {
  return 
左边
} function Right() { return
右边
} function Container(props) { return (
父组件容器
{props.left}
{props.right}
) } ReactDOM.render( } right={} />, //通过不同属性将不同组件渲染到指定位置 document.getElementById('app') )

渲染结果如下:

React学习--组件、props、state、生命周期_第1张图片

    在父组件容器Container中通过自定义属性props.left与props.right设置了两个插槽,在使用时,将left属性指定为组件,将right指定为,就将两个子组件渲染到了容器的指定位置。

4、Props的只读性

    React规定任何组件对于它的props属性只可以访问,而不可以修改

5、state

    由于组件无法对props属性进行更改,如果要实现一个动态变化的时钟,只能通过setInterval每隔一秒执行一次tick渲染函数,重新渲染组件,上层动态地传入数据,然后利用props读取显示。

function Clock(props) {
  return 
现在的时间是:{props.date.toLocaleTimeString()}
} function tick() { ReactDOM.render( , document.getElementById('app') ); } setInterval(tick,1000);

    以上重新渲染静态props很不方便,为了实现组件内部数据动态更新,需要使用React的状态(state)来储存变量,state允许组件对其进行更改。state只能在以类方式定义的组件中使用。

5.1读取state:通过this.state.date可以访问到状态date

5.2初始化state:通过类的构造函数constructor完成state的初始化

class Clock extends React.Component{
    constructor(props){
      super(props);            //调用父类的构造方法对props属性进行初始化
      this.state={
        date:new Date()        //对date初始化为一个Date对象
      }
    }
}

5.3修改state:state不可以直接对其进行修改,而是通过setState()函数对其进行修改,例如定义tick函数对date的更新

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

5.4state异步更新:state的更新是异步的,我们不能依赖它来进行实时计算。例如设置一个flag,通过按钮点击来切换flag的值,当第一次点击时setState会将flag(初始值false)取反设为true,但是由于state异步更新,它不会立即将this.flag更新为true,当再次点击时,setState会认为this.flag仍然是false,便又将flag设为true,无法将flag设为false。

this.setState({
  flag:!this.flag
});

     要弥补这个问题,使用另一种 setState() 的形式,它接受一个函数而不是一个对象。这个函数将接收前一个状态prevState作为第一个参数,这样就可以实现flag在上一个状态flag的基础上取反

        this.setState((prevState) =>({
          flag: !prevState.flag
        }))

6、列表组件

    当把一个数组内的元素渲染到页面时,需要使用列表

  • ,然而一个一个地写li太过麻烦,js的map()函数可以帮助我们便捷地对数组元素进行遍历生成list列表。react要求为每个li设置key值以区分每个不同的
  • 标签,一般可以把map回调函数中的参数index设为key值

    function Number(props) {
      let listNum=props.number.map(
        (val,index)=>                      //ES6函数的简写:(值,索引)
            
  • {val}
  • // => 遍历每个元素的返回值 ); return
      {listNum}
    ; } const numArr=['one','two','three']; ReactDOM.render( , document.getElementById('app') );

    渲染结果为:

    React学习--组件、props、state、生命周期_第2张图片

        注意:key值要求在兄弟元素之间唯一,但不同数组的元素可以相同。

                  key只用于react区分提示,元素无法通过props.key值来访问key的值。

                  JSX的大括号中可以嵌套任何表达式,因此可以直接将遍历返回的

  • 数组放到
      中,而不必通过ListNum变量。

      return (
        
        { //可直接在{}内放js表达式并返回显示的标签内容 props.number.map((val,index)=>
      • {val}
      • ) }
      );

      7、生命周期

          组件从创建到被销毁有一个生命周期:挂载、更新、销毁,在每个周期的前后都有相应的钩子函数,当组件的生命进行到对应阶段时,钩子函数就会被执行,可以在钩子函数中调用函数来完成组件在对应生命周期的操作。例如在组件挂载完成后的钩子componentDidMount(){}设置一个定时器,定时调用tick()来更新date,当state.date发生变化时,react会重新渲染组件,从而实现时钟效果。当组件被销毁前在钩子componentWillUnmount(){}内将定时器清除。

      componentDidMount(){        //组件挂载后执行
        this.timer=setInterval(()=>this.tick(),1000);
      }
      
      componentWillUnmount(){     //组件销毁前执行
        clearInterval(this.timer);
      }

      8、数据流动自顶向下

          state通常被称为局部或封装。 除了拥有并设置它的组件外,其它组件不可访问。但是它可以作为参数传递给子组件,在子组件中通过props属性可以使用从上层传递下来的值,但并且不知道它是来自 state、props还是手工输入。例如以下三种方式传递参数:

      
      
      
      function FormattedDate(props) {    //在组件中使用props.date,但并不知道上层数据的具体形式
        return 

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

      ; }

      这通常被称为自顶向下的数据流。 state始终由某些特定组件所有,并且该组件的数据只能对子组件造成影响。

  • 你可能感兴趣的:(WEB)