React 中屬性 props 和狀態 state 說明及區別

react 中通過 props 和 state 實現組件間的通信,對數據進行傳遞、操作。

1. 屬性 props

在組件中可以通過 props 傳遞數據。

正常情況下,props是外部傳入的,組件內部也可以通過一些方式來設置屬性的默認值,屬性不能被組件自己更改,但是你可以通過父組件主動重新渲染的方式來傳入新的 props。

通俗來講,就是在使用一個組件的時候,可以把參數放在標籤的屬性當中,所有的屬性都會作爲組件 props 對象的鍵值。通過箭頭函數創建的組件,需要通過函數的參數來接收props

來看一個使用 props 傳參的例子:

import React from 'react';
import ReactDOM from 'react-dom';

//函數組件
function Box(props) {
    return <h1>Hello {props.name}!</h1>;
}
 
const element = <Box name="React"/>;
 
ReactDOM.render(
    element,
    document.getElementById('root')
);

1.1 設置默認值 defaultProps

在類組件中,可以通過組件類的 defaultProps 屬性爲 props 設置默認值。在父組件沒有指定其值時,會使用這個默認值。propTypes 類型檢查發生在 defaultProps 賦值後,所以類型檢查也適用於 defaultProps。

import React from 'react';
import ReactDOM from 'react-dom';

class Box extends React.Component {
	//方式一:在類的內部設置靜態屬性
	static defaultProps={
		name:'react'
	}
	render() {
		return (
			<h1>Hello, {this.props.name},age:{this.props.age}</h1>
		);
	}
}

//方式二:在類的外面 設置defaultProps屬性
Box.defaultProps = {
	age: 18
};
 
ReactDOM.render(
  <Box/>,
  document.getElementById('root')
);

1.2 props.children

每個組件都可以獲取到 props.children。它包含組件的開始標籤和結束標籤之間的內容。類似vue中的插槽。

看下面這個例子:

import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class Content extends Component {
  render() {
    return (
      <div>{this.props.children}</div>
    );
  }
}

class Title extends Component {
  render() {
    return (
      <div>歡迎進入{this.props.children} </div>
    );
  }
}

ReactDOM.render(
	<Content>
		<Title>React</Title>
		<p>這裏是內容</p>
	</Content>,
	document.getElementById('root')
);

上面的代碼中,在頁面中渲染Content 的 props.children,最終會渲染出<Content></Content>標籤中的所有內容,渲染頁面的結果如下:
React porps

1.3 proptypes 類型檢查

Props 驗證使用 propTypes,它可以保證我們的應用組件被正確使用,React.PropTypes 提供很多驗證器 (validator) 來驗證傳入數據是否有效。當向 props 傳入無效數據時,JavaScript 控制檯會拋出警告。

React.PropTypes 在 React v15.5 版本後已經移到了prop-types 庫。

在終端中執行下面的命令,安裝 prop-types 插件
yarn add prop-types -S

使用:

  1. 使用 import 導入 prop-types。
  2. prop-types 只能在類組件中做驗證,不能在函數組件中做驗證。
  3. 通過 類名.propTypes={},來定義屬性規則,固定寫法,注意大小寫
  4. 類名.propTypes={} 中,鍵名爲屬性名,值爲對屬性的驗證規則,例如Proptypes.string等。
  5. 可以指定一個自定義驗證器。它在驗證失敗時應返回一個 Error 對象。自定義驗證時,對象值爲一個函數,函數的參數可以通過 arguments 打印。
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import Proptypes from 'prop-types';

class Box extends Component {
  render() {
    return (
      <div>
        姓名: {this.props.name},年齡:{this.props.age}
      </div>
    );
  }
}

Box.propTypes={
	name:Proptypes.string.isRequired,	//表示name爲字符串類型,isRequired 必須要傳
	age:function(props,propName){	//自定義驗證
		if(props[propName]<20){
            return new Error(
                'Invalid prop `' + propName + '` Because it Less than 20.'
              )
        }
	}
}

ReactDOM.render(
  <Box name="xiaoming" age={18} />,
  document.getElementById('root')
);

prop-types 的驗證規則:

規則 說明
.array 輸入的類型爲數組
.bool 輸入的類型爲布爾
.func 輸入的類型爲函數
.number 輸入的類型爲數值
.object 輸入的類型爲對象
.string 輸入的類型爲字符串
.symbol 輸入的類型爲symbol類型

以上爲 js 的原生類型

規則 說明
.node 表示任何可被渲染的元素(包括數字、字符串、元素或數組) (或 Fragment) 也包含這些類型。
.element 表示一個 React 元素,確保傳遞給組件的 children 中只包含一個元素。
.elementType 表示一個 React 元素類型,即上面案例中的 Box
.instanceOf() 聲明 prop 爲是否爲類的實例,這裏使用 JS 的 instanceof 操作符
.oneOf() 指定 prop 只能是特定的值,指定它爲枚舉類型
.oneOfType() 一個對象可以是幾種類型中的任意一個類型
.arrayOf() 指定一個數組由某一類型的元素組成,例如只能由數字組成的數組 .arrayOf(PropTypes.number)
.objectOf() 指定一個對象由某一類型的值組成,使用方法同 .arrayOf()
.shape() 指定一個對象由特定的類型值組成
.isRequired 在任何 PropTypes 屬性後面加上 isRequired ,確保這個 prop 沒有被提供時,會打印警告信息

部分語法使用如下:

import PropTypes from 'prop-types';

Box.propTypes = {
  // 你可以讓你的 prop 只能是特定的值,指定它爲
  // 枚舉類型。
  optionalEnum: PropTypes.oneOf(['News', 'Photos']),

  // 一個對象可以是幾種類型中的任意一個類型
  optionalUnion: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.number,
    PropTypes.instanceOf(Message)
  ]),

  // 可以指定一個數組由某一類型的元素組成
  optionalArrayOf: PropTypes.arrayOf(PropTypes.number),

  // 可以指定一個對象由某一類型的值組成
  optionalObjectOf: PropTypes.objectOf(PropTypes.number),

  // 可以指定一個對象由特定的類型值組成
  optionalObjectWithShape: PropTypes.shape({
    color: PropTypes.string,
    fontSize: PropTypes.number
  }),
  
  // An object with warnings on extra properties
  optionalObjectWithStrictShape: PropTypes.exact({
    name: PropTypes.string,
    quantity: PropTypes.number
  }),   

2. 狀態 state

state 與 props 類似,但是 state 是私有的,並且完全受控於當前組件。

可以在類的構造函數中初始化狀態 state,也可以在類中直接定義屬性(下面2.1中的寫法)來初始化狀態 state 。通過 this.state 獲取 state 中的數據內容。

import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class Box extends Component {
  constructor() {
    super();
    this.state = {
      name: 'xiaoming',
      age: 18
    }
  }
  render() {
    return (
      <div>
        姓名: {this.state.name},年齡:{this.state.age}
      </div>
    );
  }
}

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

2.1 setState()

修改 state 中的數據內容,有以下三種方式:

import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class Box extends Component {
    state={
        name:"xiaoming"
    }
    componentDidMount(){
    	//第一種修改方式
    	this.setState({name:'zhangsan'});

		//第二種修改方式
        this.state.name="sssss";
        this.setState({});

		//第三種修改方式
		this.setState((preState,props)=>{
		    return{
		        name:preState.name+'ccc'
		    }
		},()=>{ 
		    //數據修改完成後的回調函數
		})
    }
    render() {
      return (
        <div>姓名: {this.state.name}</div>
      );
    }
  }
  
ReactDOM.render(
  <Box />,
  document.getElementById('root')
);

當使用第二種方式的時候,雖然修改後的數據也在瀏覽器端渲染出來了,但是在控制檯中會輸出下面的警告信息,所以不建議直接使用 this.state 修改數據

在這裏插入圖片描述
react 和 vue 中對 state 中數據的修改都是異步的,在vue中可以使用$nextTick() 方法,獲取修改後的數據內容,在 react 中通過使用上面的第三種方法,獲取修改後的數據內容。

setState 對數據的更新,會做 merge 合併的操作,即不會覆蓋原來的數據內容,會把你提供的對象合併到當前的 state中。

2.2 狀態提升

如果多個組件要實現數據共享,可以將數據提升到父組件中,對數據的操作統一在父組件中進行。

3. 屬性和狀態的區別

props 和 state 的相似點:

  • 都是 js 對象,更新數據後會都會觸發 render() 更新;
  • 都可以設置默認值;props 通過 defaultProps 屬性設置默認值;state 直接在定義的時候設置初始值;

props 和 state 的不同點:

  • 屬性是從父組件獲取的,狀態是在當前組件中定義的;
  • 屬性值只能由父組件修改,狀態值只能由當前組件修改;
  • 屬性主要用於父組件和子組件間的通信,在組件內部是無法修改參數的;
  • 狀態是給自己用的,在內部初始化,被自己修改,在外部是不能修改的,內部通過 setState 修改,會觸發render函數;

總結:state 和 props 主要的區別在於 props 是不可變的,而 state 可以根據與用戶交互來改變。這就是爲什麼有些容器組件需要定義 state 來更新和修改數據。 而子組件只能通過 props 來傳遞數據

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章