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;
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容