日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

React組件實例三大核心屬性State?props?Refs詳解_React

作者:花鐺 ? 更新時間: 2022-12-28 編程語言

組件組件實例的三大核心屬性-State

狀態 state 是組件實例對象最重要的屬性之一,它的值是一個對象,可以包含多個 key-value 的組合。

當組件中的一些數據在某些時刻發生變化時,就需要使用 state 來跟蹤狀態。state 是私有的,并且完全受控于當前組件,除了擁有并設置了它的組件,其他組件都無法訪問。

組件被稱為狀態機,通過更新組件的 state 來重新渲染組件,更新對應的頁面顯示。

this.props 和 this.state 是 React 本身設置的,且都擁有特殊的含義,但是其實可以向類組件中隨意添加不參與數據流的額外字段(比如 this.timerID)。

state 和 props 之間最重要的區別是:

  • props 由父組件傳入,而 state 由組件本身管理。
  • 組件不能修改 props,但可以修改 state。
class Weather extends React.Component{
	constructor(props) {
	    super(props)
	    // 初始化 state
	    this.state = {
	      isHot: false,
	    }
	}
	// state可以簡寫成如下形式
	// 原因是:類中可以直接寫賦值語句,實際上就是直接給實例對象上添加屬性
	state = {
		isHot: false,
	}
	componentDidMount() {
	    // 更改 state
	    this.setState({
	        isHot: !this.state.isHot,
	     })
	  }
	 ...
}

State 不可以直接修改

初始化 state 之后,在其他地方不可以直接修改 state,而是應該使用 React 內置的一個 API:setState() 來修改。

constructor() 只初始化的時候調用一次。

render() 會調用 1+n 次,1是初始化,n 是狀態更新的次數(也就是說,每次 setState() 之后, React 都會調用一次 render())。

// Wrong,此代碼不會重新渲染組件
this.state.comment = 'Hello';
// Correct
this.setState({comment: 'Hello'});

setState() 有兩種寫法:

setState(nextState, [callback]):對象式的 setState。

參數:

  • nextState:將要設置的新狀態,該狀態會和當前的 state 合并。
  • callback:可選參數,回調函數。該函數會在狀態更新完畢,且界面也更新后(render() 后)調用。
	this.setState({
		count: this.state.count +1,
	}, () => {
		console.log(this.state.count)
	})

setState(updater, [callback]):函數式的 setState。

參數:

  • updater:是一個函數,可以接收到 state 和 props 作為參數,返回值為將要設置的新狀態。
  • callback:可選參數,回調函數。該函數會在狀態更新完畢,且界面也更新后(render() 后)調用。
	this.setState((state, props) => ({
		count: state.count +1,
	}), () => {
		console.log(this.state.count)
	})

對象式的 setState 是函數式的 setState 的簡寫方式(語法糖)。這兩種寫法的使用原則:

如果新狀態不依賴于原狀態,使用對象方式;如果新狀態依賴于原狀態,使用函數方式。如果需要在 setState() 執行后獲取最新的狀態數據,要在第二個參數 callback 函數中讀取。

State 的更新是合并

setState() 的更新是合并,不是替換。

constructor(props) {
    super(props);
    // state 包含幾個獨立的變量
    this.state = {
      isHot: false,
       wind: '微風',
    }
  }
componentDidMount() {
    // 此處調用 setState() 更新了 isHot 的值,但是 wind 的值也并沒有丟失,所以說明更新的這個動作是合并
    this.setState({
        isHot: !this.state.isHot,
     })
  }

State 的更新可能是異步的

出于性能考慮,React 可能會把多個 setState() 調用合并成一個調用。

因為 this.props 和 this.state 可能會異步更新,所以不要依賴他們的值來更新下一個狀態。要解決這個問題,可以讓 setState() 接收一個函數而不是一個對象。

// Wrong
this.setState({
  count: this.state.count + 1,
})
console.log(this.state.counter) //此時直接讀取獲取到的仍然是舊的 count 值
// Correct
this.setState({
  count: this.state.count + 1,
}, () => {
	console.log(this.state.counter) //此時讀取獲取到的是新的 count 值
})

組件實例對象的三大核心屬性-Props

當 React 元素為用戶的自定義組件時,它會將所接收的標簽屬性及子組件轉換為單個對象傳遞給組件,這個對象被稱之為 “props”。

props 是 React 組件的輸入。它們是組件外部向組件內部傳遞變化的數據。

props 是只讀的,組件無論是使用函數組件還是類組件,都決不能修改自身的 props。

// 類組件
class Person extends React.Component{
	render(){
		const {name, age, sex} = this.props
		return (
			<ul>
				<li>姓名:{name}</li>
				<li>性別:{sex}</li>
				<li>年齡:{age+1}</li>
			</ul>
		)
	}
}
// 函數式組件
function Person(props){
	const {name, age, sex} = props
	return (
		<ul>
			<li>姓名:{name}</li>
			<li>性別:{sex}</li>
			<li>年齡:{age+1}</li>
		</ul>
	)
}
ReactDOM.render(<Person name="jerry" age={19}  sex="男"/>, document.getElementById('test'))
// 批量傳遞 props 的簡寫方法(或者叫批量傳遞標簽屬性):
// 原生 JS 中擴展運算符是不能展開對象的。
// 由于 React 和 Babel 的原因,擴展運算符可以展開對象,但僅僅適用于標簽屬性的傳遞,別的地方不支持。
ReactDOM.render(<Person {...{
	name: 'jerry',
	age: 19,
	sex: '男'
}} />, document.getElementById('test'))

props.children

每個組件都可以獲取到 props.children,它包含組件的開始標簽和結束標簽之間的內容。

<Welcome>Hello world!</Welcome>
// 不寫標簽體,寫成 children 標簽屬性也可以
<Welcome children='Hello world!'></Welcome>
// 在 Welcome 組件中獲取 props.children,就可以得到字符串 Hello world!
function Welcome(props) {
  return <p>{props.children}</p>;
}

使用defaultProps設置默認的prop值

可以通過配置特定的 defaultProps 屬性來定義 props 的默認值。

// 給組件加上 defaultProps 的屬性
Person.defaultProps = {
  title: '我是詳情'
}
// 簡寫:簡寫的這種方式只適用于類組件,因為函數式組件中是沒有 static 的
class Person extends React.Component{
	static defaultProps = {
	  title: '我是詳情'
	}
}

使用propTypes進行類型檢查

PropTypes 提供一系列驗證器,可用于確保組件接收到的數據類型是有效的。當傳入的 prop 值類型不正確時,JavaScript 控制臺將會顯示警告。

propTypes 類型檢查發生在 defaultProps 賦值后,所以類型檢查也適用于 defaultProps。

出于性能方面的考慮,propTypes 僅在開發模式下進行檢查。

// 只要給組件加上 propTypes 屬性,React 就會認為是在加規則
Person.propTypes = {
  // React.PropTypes 是 React 內置的屬性
  title: React.PropTypes.string.isRequired, // 錯誤
   // 自 React v15.5 起,React.PropTypes 已移入另一個包中,需要的話要使用`import PropTypes from 'prop-types'`引入,引入之后全局就會有了一個對象 PropTypes
    title: PropTypes.string.isRequired, // 正確
    speak: PropTypes.func,
}
/// 簡寫:簡寫的這種方式只適用于類組件,因為函數式組件中是沒有 static 的
class Person extends React.Component{
	static propTypes = {
	  title: PropTypes.string.isRequired, 
	}
}

組件實例對象的三大核心屬性-Refs

PS:勿過度使用 Refs

組件內的標簽可以定義 ref 屬性來標識自己,都會被收集到組件實例對象的 refs 屬性下,這樣,通過 this.refs.ref屬性 就可以訪問到 ref 當前所處的真實節點。

無法在函數式組件上使用 ref 屬性。

Ant Design 中很多組件都獲取不到 ref,可以包裹或內嵌一層自己創建的元素以獲取 ref。

字符串形式的Ref

React 不推薦使用字符串形式的 ref,它已過時并可能會在未來的版本中被移除,這種方式存在一些效率上的問題。

class Demo extends React.Component {
  showData = () => {
     console.log(this) // 打印可以看到組件的實例對象上 this 有 refs 屬性,屬性值是 key-value 的對象 ,其中有一個key 就是 input1,value 是 ref 當前所處的真實節點。
	// 訪問 refs
     alert(this.res.input1.value)
  }
  render() {
    return (
    	<div>
    	    // 創建、綁定 refs
    		<input ref="input1" />
    		<button onClick={this.showData}>點擊</button>
    	</div>
    )
  }
}

回調函數形式的Ref

如果 ref 回調函數是以內聯函數的方式定義的,在更新過程中它會被執行兩次,第一次傳入參數 null,第二次才會傳入 DOM 元素。這是因為在每次 render 渲染時都會創建一個新的函數實例,所以 React 會首先清空舊的 ref,然后才會設置新的。這個問題大多數情況下是無關緊要的。

初次渲染時不會,因為初次渲染時沒有舊的 ref 需要去清空。

class Demo extends React.Component {
  showData = () => {
	// 訪問 refs
     alert(this.input1.value)
  }
  render() {
    return (
    	<div>
    	    // 創建、綁定 refs
    	    // render 方法執行的時候會自動調用 ref 的回調函數,并且會把當前所處的真實節點作為參數傳遞進去,然后將這個節點賦值給組件實例自身的一個自定義屬性上
    		<input ref={c => this.input1 = c} />    
    		<button onClick={this.showData}>點擊</button>
    	</div>
    )
  }
}

通過將 ref 的回調函數定義成類的綁定函數的方式可以避免上述問題。

class Demo extends React.Component {
 setInputRef = (c) => {
 	// 綁定 refs
 	this.input1 = c
 }
  showData = () => {
	// 訪問 refs
     alert(this.input1.value)
  }
  render() {
    return (
    	<div>
    	    // 創建 refs
    	    // 更新時也不會重復觸發 setInputRef,因為它已經放在實例自身了
    		<input ref={this.setInputRef} />    
    		<button onClick={this.showData}>點擊</button>
    	</div>
    )
  }
}

createRef

React.createRef() 是 React 內置的一個 API,調用后可以返回一個容器,該容器存儲被 ref 所標識的節點。該容器是專人專用的。

class Demo extends React.Component {
   // 創建 refs
  myRef = React.createRef()
  showData = () => {
  	// 訪問 refs
     alert(this.myRef.current.value)
  }
   render() {
     return (
    	<div>
    	    // 綁定 refs
    	    // 下面一行代碼在執行的時候,React 發現了 ref 屬性,并且發現屬性值是用 createRef 創建出來的一個容器,這時, React 會把當前 ref 所在的那個節點直接存儲到那個容器里面
    		<input ref={this.myRef} />    
    		<button onClick={this.showData}>點擊</button>
    	</div>
    )
  }
}

訪問Refs

當 ref 被傳遞給 render 中的元素時,對該節點的引用可以在 ref 的 current 屬性中被訪問。

ref 的值根據節點

Refs 轉發

Refs 轉發是一個可選特性,其允許某些組件接收 ref,并將其向下傳遞給子組件。

ref 轉發不僅限于 DOM 組件,也可以轉發 refs 到 class 組件實例。

const ref = React.createRef();
<FancyButton ref={ref}>Click me!</FancyButton>;
const FancyButton = React.forwardRef((props, ref) => (
  <button ref={ref} className="FancyButton">
    {props.children}
  </button>
));

FancyButton 使用 React.forwardRef 來獲取傳遞給它的 ref,然后轉發到它渲染的 DOM button。這樣,使用 FancyButton 的組件可以獲取底層 DOM 節點 button 的 ref ,并在必要時訪問,就像其直接使用 DOM button 一樣。

上述代碼的執行步驟如下:

  • 通過調用 React.createRef 創建了一個 React ref 并將其賦值給 ref 變量;
  • 通過指定 ref 為 JSX 屬性,將其向下傳遞給 <FancyButton ref={ref}>
  • React 傳遞 ref 給 forwardRef 內函數 (props, ref) => ...,作為其第二個參數;
  • 向下轉發該 ref 參數到 <button ref={ref}>,將其指定為 JSX 屬性;
  • 當 ref 掛載完成,ref.current 將指向 <button> DOM 節點;

原文鏈接:https://blog.csdn.net/wsln_123456/article/details/112005521

欄目分類
最近更新