React属性(props)和状态(state)

React属性(props)和状态(state)

一、属性(props)

属性props是由外部传入、描述性质的,组件内部也可以通过一些方式来初始化的设置。属性不能被组件自己更改,但是可以通过父组件主动重新渲染的方式来传入新的 props

之前的组件代码里面有props的简单使用,总的来说,在使用一个组件的时候,可以把参数放在标签的属性当中,所有的属性都会作为组件 props 对象的键值。通过箭头函数创建的组件,需要通过函数的参数来接收props

  • 设置组件的默认props
import React, { Component } from 'react';

class Title extends Component {

//第一种定义方式
  static defaultProps = {
    name:"react"
  }

  render() {
    return (
      <div>
        欢迎进入{this.props.name} 的世界
      </div>
    );
  }
}
//第二种方式
// Title.defaultProps = {
//   name:'react'
// }

export default Title;
  • props.children(插槽)

组件可以嵌套,在自定义组件使用嵌套结构时,需要使用 props.children

ReactDOM.render(
  <Content>
    <h1>lxc</h1>
    <Title>
      react
    </Title>
  </Content>,
  document.getElementById('root')
);

通过“插槽”进行嵌套内容的接收

import React, { Component } from 'react';
class Content extends Component {
  render() {
    return (
      <div>
        {
          this.props.children
        }
      </div>
    );
  }
}
export default Content;
  • 使用prop-types检查props

React其实是为了构建大型应用程序而生, 在一个大型应用中,根本不知道别人使用你写的组件的时候会传入什么样的参数,有可能会造成应用程序运行不了,但是不报错。为了解决这个问题,React提供了一种机制,让写组件的人可以给组件的props设定参数检查,需要安装和使用prop-types:

  • 安装prop-types验证
$ cnpm i prop-types -S
import React, { Component } from 'react';
import Proptypes from 'prop-types'

class Product extends Component {
  render() {
    return (
      <div>
        产品名称: {this.props.name}
      </div>
    );
  }
}

Product.propTypes={
  name:Proptypes.string,
  //['北京','天津']
  // city:Proptypes.arrayOf(Proptypes.string).isRequired,
  // customProp:function(props,PropName){
  //   if(!/gp/.test(props[PropName])){
  //     return new Error('内容非法')
  //   }
  //   console.log(arguments)
  // },
  customArrayProp: Proptypes.arrayOf(function(propValue, key, componentName, location, propFullName) {
    if (!/北京/.test(propValue[key])) {
      return new Error(
        'Invalid prop `' + propFullName + '` supplied to' +
        ' `' + componentName + '`. Validation failed.'
      );
    }
  }).isRequired
}

export default Product;

二、状态(state)

状态就是组件描述某种显示情况的数据,由组件自己设置和更改,也就是说由组件自己维护,使用状态的目的就是为了在不同的状态下使组件的显示不同(自己管理)

  • 定义state
 constructor() {
    super();
    //state定义的第一种方式
    //推荐
    // this.state = {
    //   count: 1,
    //   title: "中国机长"
    // }
  }
//state定义的第二种方式
  state ={
    title:"中国机长",
    count:1
  }
  • 修改state的三种方式
  1. 在React中通过this.setState进行数据的修改。
  2. this.setState是异步执行的。( 异步转同步 )
  3. this.setState中的第二个参数是回调,用来验证数据是否修改成功,获取数据更新后的DOM结构。
  4. 只要this.setState调用render函数就会执行。
import React, { Component } from 'react';
class Movie extends Component {

  constructor() {
    super();
    this.state ={
        title:"中国机长",
        count:1
     }
  }

  componentDidMount() {
      
    //修改state的第一种方式
    // setTimeout(() => {
    //   this.setState({
    //     title: "战狼2"
    //   })
    // }, 2000)

    //修改state的第二种方式
    // this.state.title ="战狼2"
    // setTimeout(()=>{
    //   this.setState({})
    // },2000)

    //修改state的数据是异步的
    // this.setState({
    //   count: this.state.count + 1
    // })
    // console.log(this.state.count)

    //修改state的第三种方式
    this.setState((preState, props) => {
      console.log(props);
      //数据的更新,会做merge
      return {
        count: preState.count + 1
      }
    }, () => {
      console.log(this.state.count)
    })

    setTimeout(()=>{
      this.setState({
        count:3
      })
      console.log(this.state.title,this.props.city)
    },3000)
  }

  render() {
    console.log(1)
    return (
      <div>
        <h1>{this.state.title}</h1>
      </div>
    );
  }
}

export default Movie;

三、属性vs状态

  • 相似点:

都是纯JS对象,都会触发render更新,都具有确定性(状态/属性相同,结果相同)

  • 不同点:
  1. 属性能从父组件获取,状态不能
  2. 属性可以由父组件修改,状态不能
  3. 属性能在内部设置默认值,状态也可以
  4. 属性不在组件内部修改,状态要改
  5. 属性能设置子组件初始值,状态不可以
  6. 属性可以修改子组件的值,状态不可以

state 的主要作用是用于组件保存、控制、修改自己的可变状态。state 在组件内部初始化,可以被组件自身修改,而外部不能访问也不能修改。你可以认为 state 是一个局部的、只能被组件自身控制的数据源。state 中状态可以通过 this.setState方法进行更新,setState 会导致组件的重新渲染。

props 的主要作用是让使用该组件的父组件可以传入参数来配置该组件。它是外部传进来的配置参数,组件内部无法控制也无法修改。除非外部组件主动传入新的 props,否则组件的 props 永远保持不变。

如果搞不清 stateprops 的使用场景,记住一个简单的规则:尽量少地用 state,多用 props

没有 state 的组件叫无状态组件(stateless component),设置了 state 的叫做有状态组件(stateful component)。因为状态会带来管理的复杂性,我们尽量多地写无状态组件,尽量少地写有状态的组件。这样会降低代码维护的难度,也会在一定程度上增强组件的可复用性。

四、状态提升

如果有多个组件共享一个数据,把这个数据放到共同的父级组件中来管理

五、案例(计数器)

  • Counter.jsx
import React, { Component } from 'react';
import Button from './Button'
import Count from './Count'
import styled from 'styled-components'

const CountWrapper = styled.div`
  h1 {
    display:block;
    color:red;
  }

  button {
    width:100px;
    height:40px;
    background:blue;
    color:white;
    font-size:20px;
  }
`

class Counter extends Component {

  constructor() {
    super();
    this.state = {
      count: 0
    }
  }

  handleChange(type) {
    console.log('handle change')
    this.setState((preState, props) => {
      if (type === 'increment') {
        return {
          count: preState.count + 1
        }
      } else {
        return {
          count: preState.count - 1
        }
      }

    }, () => { })
  }

  render() {
    return (
      <CountWrapper >
        <Button change={() => this.handleChange('decrement')}>-</Button>
        <Count count={this.state.count}></Count>
        <Button change={() => this.handleChange('increment')}>+</Button>
      </CountWrapper>
    );
  }
}

export default Counter;
  • Count.jsx
import React from 'react'
import Proptypes from 'prop-types'

class Count extends React.Component {

  render(){
    return <h1>{this.props.count}</h1>
  }
}

Count.propTypes ={
  count:Proptypes.number.isRequired
}

export default Count;
  • Button.jsx
import React, { Component } from 'react';

class Button extends Component {

  handleClick(){
    console.log('handle click')
    this.props.change()
  }

  render() {
    return (
     <button onClick={()=>this.handleClick()}>
       {this.props.children}
     </button>
    );
  }
}

export default Button;
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,635评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,628评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,971评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,986评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,006评论 6 394
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,784评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,475评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,364评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,860评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,008评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,152评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,829评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,490评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,035评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,156评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,428评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,127评论 2 356

推荐阅读更多精彩内容