屬性(props)
props
是正常是外部傳入的畜份,組件內部也可以通過一些方式來初始化的設置,屬性不能被組件自己更改拜秧,但是你可以通過父組件主動重新渲染的方式來傳入新的 props
屬性是描述性質少漆、特點的厅须,組件自己不能隨意更改。
之前的組件代碼里面有props
的簡單使用危尿,總的來說呐萌,在使用一個組件的時候,可以把參數(shù)放在標簽的屬性當中谊娇,所有的屬性都會作為組件 props
對象的鍵值肺孤。通過箭頭函數(shù)創(chuàng)建的組件,需要通過函數(shù)的參數(shù)來接收props
:
import React, { Component, Fragment } from 'react'
import ReactDOM from 'react-dom'
class Title extends Component {
render () {
return (
<h1>歡迎學習{this.props.name}</h1>
)
}
}
const Content = (props) => {
return (
<p>{props.name}是一個構建UI的庫</p>
)
}
class App extends Component {
render () {
return (
<Fragment>
<Title name="React" />
<Content name="React.js" />
</Fragment>
)
}
}
ReactDOM.render(
<App/>,
document.getElementById('root')
)
設置組件的默認props
import React, { Component, Fragment } from 'react'
import ReactDOM from 'react-dom'
class Title extends Component {
// 使用類創(chuàng)建的組件邮绿,直接在這里寫static方法渠旁,創(chuàng)建defaultProps
static defaultProps = {
name: 'React'
}
render () {
return (
<h1>歡迎學習{this.props.name}</h1>
)
}
}
const Content = (props) => {
return (
<p>{props.name}是一個構建UI的庫</p>
)
}
// 使用箭頭函數(shù)創(chuàng)建的組件,需要在這個組件上直接寫defaultProps屬性
Content.defaultProps = {
name: 'React.js'
}
class App extends Component {
render () {
return (
<Fragment>
{/* 由于設置了defaultProps船逮, 不傳props也能正常運行顾腊,如果傳遞了就會覆蓋defaultProps的值 */}
<Title />
<Content />
</Fragment>
)
}
}
ReactDOM.render(
<App/>,
document.getElementById('root')
)
props.children
我們知道使用組件的時候,可以嵌套挖胃。要在自定義組件的使用嵌套結構杂靶,就需要使用 props.children
梆惯。在實際的工作當中,我們幾乎每天都需要用這種方式來編寫組件吗垮。
import React, { Component, Fragment } from 'react'
import ReactDOM from 'react-dom'
class Title extends Component {
render () {
return (
<h1>歡迎學習{this.props.children}</h1>
)
}
}
const Content = (props) => {
return (
<p>{props.children}</p>
)
}
class App extends Component {
render () {
return (
<Fragment>
<Title>React</Title>
<Content><i>React.js</i>是一個構建UI的庫</Content>
</Fragment>
)
}
}
ReactDOM.render(
<App/>,
document.getElementById('root')
)
使用prop-types檢查props
React其實是為了構建大型應用程序而生, 在一個大型應用中垛吗,根本不知道別人使用你寫的組件的時候會傳入什么樣的參數(shù),有可能會造成應用程序運行不了烁登,但是不報錯怯屉。為了解決這個問題,React提供了一種機制饵沧,讓寫組件的人可以給組件的props
設定參數(shù)檢查锨络,需要安裝和使用prop-types:
$ npm i prop-types -S
狀態(tài)(state)
狀態(tài)就是組件描述某種顯示情況的數(shù)據(jù),由組件自己設置和更改狼牺,也就是說由組件自己維護羡儿,使用狀態(tài)的目的就是為了在不同的狀態(tài)下使組件的顯示不同(自己管理)
定義state
第一種方式
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
class App extends Component {
state = {
name: 'React',
isLiked: false
}
render () {
return (
<div>
<h1>歡迎學習{this.state.name}</h1>
<button>
{
this.state.isLiked ? '??取消' : '??收藏'
}
</button>
</div>
)
}
}
ReactDOM.render(
<App/>,
document.getElementById('root')
)
另一種方式(推薦)
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
class App extends Component {
constructor() {
super()
this.state = {
name: 'React',
isLiked: false
}
}
render () {
return (
<div>
<h1>歡迎學習{this.state.name}</h1>
<button>
{
this.state.isLiked ? '??取消' : '??收藏'
}
</button>
</div>
)
}
}
ReactDOM.render(
<App/>,
document.getElementById('root')
)
this.props
和this.state
是純js對象,在vue中,data屬性是利用Object.defineProperty
處理過的是钥,更改?data的數(shù)據(jù)的時候會觸發(fā)數(shù)據(jù)的getter
和setter
掠归,但是React中沒有做這樣的處理,如果直接更改的話悄泥,react是無法得知的虏冻,所以,需要使用特殊的更改狀態(tài)的方法setState
码泞。
setState
isLiked
存放在實例的 state
對象當中兄旬,組件的 render
函數(shù)內,會根據(jù)組件的 state
的中的isLiked
不同顯示“取消”或“收藏”內容余寥。下面給 button
加上了點擊的事件監(jiān)聽。
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
class App extends Component {
constructor() {
super()
this.state = {
name: 'React',
isLiked: false
}
}
handleBtnClick = () => {
this.setState({
isLiked: !this.state.isLiked
})
}
render () {
return (
<div>
<h1>歡迎學習{this.state.name}</h1>
<button onClick={this.handleBtnClick}>
{
this.state.isLiked ? '??取消' : '??收藏'
}
</button>
</div>
)
}
}
ReactDOM.render(
<App/>,
document.getElementById('root')
)
setState
有兩個參數(shù)
第一個參數(shù)可以是對象悯森,也可以是方法return一個對象宋舷,我們把這個參數(shù)叫做updater
-
參數(shù)是對象
this.setState({ isLiked: !this.state.isLiked })
-
參數(shù)是方法
this.setState((prevState, props) => { return { isLiked: !prevState.isLiked } })
注意的是這個方法接收兩個參數(shù),第一個是上一次的state, 第二個是props
setState
是異步的瓢姻,所以想要獲取到最新的state祝蝠,沒有辦法獲取,就有了第二個參數(shù)幻碱,這是一個可選的回調函數(shù)
this.setState((prevState, props) => {
return {
isLiked: !prevState.isLiked
}
}, () => {
console.log('回調里的',this.state.isLiked)
})
console.log('setState外部的',this.state.isLiked)
屬性vs狀態(tài)
相似點:都是純js對象绎狭,都會觸發(fā)render更新,都具有確定性(狀態(tài)/屬性相同褥傍,結果相同)
不同點:
- 屬性能從父組件獲取儡嘶,狀態(tài)不能
- 屬性可以由父組件修改,狀態(tài)不能
- 屬性能在內部設置默認值恍风,狀態(tài)也可以
- 屬性不在組件內部修改蹦狂,狀態(tài)要改
- 屬性能設置子組件初始值誓篱,狀態(tài)不可以
- 屬性可以修改子組件的值,狀態(tài)不可以
state
的主要作用是用于組件保存凯楔、控制窜骄、修改自己的可變狀態(tài)。state
在組件內部初始化摆屯,可以被組件自身修改邻遏,而外部不能訪問也不能修改。你可以認為 state
是一個局部的虐骑、只能被組件自身控制的數(shù)據(jù)源党远。state
中狀態(tài)可以通過 this.setState
方法進行更新,setState
會導致組件的重新渲染富弦。
props
的主要作用是讓使用該組件的父組件可以傳入?yún)?shù)來配置該組件沟娱。它是外部傳進來的配置參數(shù),組件內部無法控制也無法修改腕柜。除非外部組件主動傳入新的 props
济似,否則組件的 props
永遠保持不變。
如果搞不清 state
和 props
的使用場景盏缤,記住一個簡單的規(guī)則:盡量少地用 state
砰蠢,多用 props
。
沒有 state
的組件叫無狀態(tài)組件(stateless component)唉铜,設置了 state 的叫做有狀態(tài)組件(stateful component)台舱。因為狀態(tài)會帶來管理的復雜性,我們盡量多地寫無狀態(tài)組件潭流,盡量少地寫有狀態(tài)的組件竞惋。這樣會降低代碼維護的難度,也會在一定程度上增強組件的可復用性灰嫉。
狀態(tài)提升
如果有多個組件共享一個數(shù)據(jù)拆宛,把這個數(shù)據(jù)放到共同的父級組件中來管理
受控組件與非受控組件
React組件的數(shù)據(jù)渲染是否被調用者傳遞的props
完全控制,控制則為受控組件讼撒,否則非受控組件浑厚。
渲染數(shù)據(jù)
- 條件渲染
{
condition ? '??取消' : '??收藏'
}
- 列表渲染
// 數(shù)據(jù)
const people = [{
id: 1,
name: 'Leo',
age: 35
}, {
id: 2,
name: 'XiaoMing',
age: 16
}]
// 渲染列表
{
people.map(person => {
return (
<dl key={person.id}>
<dt>{person.name}</dt>
<dd>age: {person.age}</dd>
</dl>
)
})
}
React的高效依賴于所謂的 Virtual-DOM,盡量不碰 DOM根盒。對于列表元素來說會有一個問題:元素可能會在一個列表中改變位置钳幅。要實現(xiàn)這個操作,只需要交換一下 DOM 位置就行了炎滞,但是React并不知道其實我們只是改變了元素的位置敢艰,所以它會重新渲染后面兩個元素(再執(zhí)行 Virtual-DOM ),這樣會大大增加 DOM 操作厂榛。但如果給每個元素加上唯一的標識盖矫,React 就可以知道這兩個元素只是交換了位置丽惭,這個標識就是key
,這個 key
必須是每個元素唯一的標識
- dangerouslySetHTML
對于富文本創(chuàng)建的內容辈双,后臺拿到的數(shù)據(jù)是這樣的:
content = "<p>React.js是一個構建UI的庫</p>"
處于安全的原因责掏,React當中所有表達式的內容會被轉義,如果直接輸入湃望,標簽會被當成文本换衬。這時候就需要使用dangerouslySetHTML
屬性,它允許我們動態(tài)設置innerHTML
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
class App extends Component {
constructor() {
super()
this.state = {
content : "<p>React.js是一個構建UI的庫</p>"
}
}
render () {
return (
<div
// 注意這里是兩個下劃線 __html
dangerouslySetInnerHTML={{__html: this.state.content}}
/>
)
}
}
ReactDOM.render(
<App/>,
document.getElementById('root')
)