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

1、定义组件

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

function MsgBox(props){
  return <div>hello {props.name}</div>
}

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

class MsgBox extends React.Component{
  render() {
    return <h1>Hello {this.props.name}</h1>
  }
}

   注意:组件名称必须以大写字母开头。例如<div />表示一个DOM标签,但<Welcome />表示一个组件,并且在使用该组件前必须定义或引入它。

2、渲染组件

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

ReactDOM.render(
  <MsgBox />,                            //要渲染的组件
  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= <p>欢迎你!</p>
    }else{
      prompt= <p>请登陆!</p>
    }

    return(
      <div>
        {prompt}                //渲染变量prompt
        <button onClick={this.shiftLog}>{this.state.hasLogin?'登出':'登录'}</button>
      </div>
    )
  }
}

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

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

  return <p>show warning</p>
}
ReactDOM.render(
  <Warning hide="true"/>,
  document.getElementById('app')
);

3、组合组件

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

//子组件
function Child(props) {
  return <p>这是{props.name}</p>;
}
//父组件
function Parent() {
  return (
    <div>                        //返回一个根标签
        <Child name="子组件1" />
        <Child name="子组件2"/>
    </div>
  )
}

ReactDOM.render(
  <Parent/>,                            
  document.getElementById('app')
  }
)

    渲染结果:

    

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

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

function Parent(props) {
  return (
    <div className="parent">
        父组件
      {props.children}        //将传入不同的children属性渲染在此位置
    </div>
  )
}

function Child() {
  return <div className="child">子组件</div>
}

function Composition() {
  return(        
    <Parent children={<Child/>}/>      //通过属性的方式传递<Child/>模块渲染到父容器中
  )
}

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

function Left() {
  return <div>左边</div>
}
function Right() {
  return <div>右边</div>
}

function Container(props) {
  return (
    <div className="container">
        <div>父组件容器</div>
        <div className="left">{props.left}</div>
        <div className="right">{props.right}</div>
    </div>
  )
}

ReactDOM.render(
  <Container left={<Left/>} right={<Right/>} />,    //通过不同属性将不同组件渲染到指定位置
  document.getElementById('app')
)

渲染结果如下:

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

4、Props的只读性

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

5、state

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

function Clock(props) {
  return <div>现在的时间是:{props.date.toLocaleTimeString()}</div>
}

function tick() {
  ReactDOM.render(
    <Clock date={new Date()}/>,
    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>,然而一个一个地写li太过麻烦,js的map()函数可以帮助我们便捷地对数组元素进行遍历生成list列表。react要求为每个li设置key值以区分每个不同的<li>标签,一般可以把map回调函数中的参数index设为key值

function Number(props) {
  let listNum=props.number.map(
    (val,index)=>                      //ES6函数的简写:(值,索引)
        <li key={index}>{val}</li>     //                 => 遍历每个元素的返回值
  );
  return <ul>
    {listNum}
  </ul>;
}

const numArr=['one','two','three'];
ReactDOM.render(
  <Number number={numArr} />,
  document.getElementById('app')
);

渲染结果为:

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

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

              JSX的大括号中可以嵌套任何表达式,因此可以直接将遍历返回的<li>数组放到<ul>中,而不必通过ListNum变量。

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

7、生命周期

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

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

componentWillUnmount(){     //组件销毁前执行
  clearInterval(this.timer);
}

8、数据流动自顶向下

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

<FormattedDate date={this.state.date} />
<FormattedDate date={this.props.date} />
<FormattedDate date="2018-04-06T10:58:41" />
function FormattedDate(props) {    //在组件中使用props.date,但并不知道上层数据的具体形式
  return <h2>It is {props.date.toLocaleTimeString()}.</h2>;
}

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值