第七節(jié):React列表渲染與數(shù)據(jù)收集

1. 列表渲染

1.1 列表渲染說明:
  1. 列表渲染 先將列表數(shù)據(jù)轉(zhuǎn)為React元素列表, 然后在渲染
  2. React會將React元素列表自動展開渲染


1.2 React元素列表渲染

說明:

  1. React元素列表是一個數(shù)組
  2. 只不過數(shù)組的每一項都是JSX語法的React元素


示例代碼:

// 不使用到組件狀態(tài),因此定義函數(shù)組件測試
function MyCom(){
    // 1. react元素列表,數(shù)組的每一項都是JSX語法的React元素
    // 將react元素列表保存在變量中
    let oList = [
        <li>蘋果</li>,
        <li>香蕉</li>,
        <li>梨子</li>
    ]
    
    // 2.在react元素中使用{}語法將列表渲染
    // react會自動遍歷列表,并將列表中的react元素進行渲染
    return <ul> { oList } </ul>
}



// 將組件渲染到頁面上
ReactDOM.render(<MyCom /> ,document.getElementById("root"))


1.3 渲染列表數(shù)據(jù)

說明:

  1. React會將react元素列表遍歷渲染
  2. 因此如果要渲染列表數(shù)據(jù),就可以先將列表數(shù)據(jù)轉(zhuǎn)為react元素列表
  3. 遍歷列表的方式有很多中,普通for循環(huán), for-in, for-of,forEach,map,reduce都可以使用


1.3.1 使用普通for循環(huán)遍歷列表數(shù)據(jù)

示例代碼:

function MyCom(){
    // 1. 列表數(shù)據(jù)
    let listdata = ["蘋果","香蕉","梨子"]


    // 2. 普通for循環(huán)
    let oList = [];
    for(let i = 0; i< listdata.length ;i++){
        // 循環(huán)列表數(shù)據(jù),將每一個數(shù)據(jù)包裹一層react元素,
        // 在push到新的oList數(shù)組中,oList就是react元素列表
        oList.push(<li>{listdata[i]}</li>)
    }
    return <ul> { oList } </ul>
}


1.3.2 使用for-in遍歷列表數(shù)據(jù)

示例代碼:

function MyCom(){
    
    // 1. 列表數(shù)據(jù)
    let listdata = ["蘋果","香蕉","梨子"]


    // 2. for-in循環(huán)
    let oList = [];

    for(let index in listdata){
        // index 為listdata列表的索引
        // 通過索引取出數(shù)據(jù)包裹react元素,組成react元素列表
        oList.push(<li>{listdata[index]}</li>)
    }

    return <ul> { oList } </ul>
}

// 將組件渲染到頁面上
ReactDOM.render(<MyCom /> ,document.getElementById("root"))


1.3.3 使用for-of遍歷列表數(shù)據(jù)

示例代碼:

function MyCom(){

    // 1. 列表數(shù)據(jù)
    let listdata = ["蘋果","香蕉","梨子"]


    // 2. for-of循環(huán)
    let oList = [];

    for(let value of listdata){
        // for-of 直接取出數(shù)據(jù),處理為react元素.列表
        oList.push(<li>{value}</li>)
    }

    return <ul> { oList } </ul>
}




// 將組件渲染到頁面上
ReactDOM.render(<MyCom /> ,document.getElementById("root"))


1.3.4 使用forEach遍歷列表數(shù)據(jù)

示例代碼如下:

function MyCom(){

    // 1. 列表數(shù)據(jù)
    let listdata = ["蘋果","香蕉","梨子"]


    // 2. forEach循環(huán)
    let oList = [];

    // forEach循環(huán)將列表數(shù)據(jù)轉(zhuǎn)為react元素列表
    listdata.forEach(value => {
        oList.push(<li>{value}</li>)
    })

    return <ul> { oList } </ul>

}


// 將組件渲染到頁面上
ReactDOM.render(<MyCom /> ,document.getElementById("root"))


1.3.5 使用map遍歷列表數(shù)據(jù)

map會自動返回列表,因此不用使用push方法

示例代碼如下:

function MyCom(){

    // 1. 列表數(shù)據(jù)
    let listdata = ["蘋果","香蕉","梨子"]


    // 2. map循環(huán)
    // map 返回的就是回調(diào)函數(shù)return內(nèi)容組成的數(shù)組
    let oList = listdata.map(value => {
        return <li>{ value }</li>
    })

    return <ul> { oList } </ul>

}


// 將組件渲染到頁面上
ReactDOM.render(<MyCom /> ,document.getElementById("root"))


1.3.6 使用reduce遍歷列表數(shù)據(jù)

示例代碼:

function MyCom(){

    // 1. 列表數(shù)據(jù)
    let listdata = ["蘋果","香蕉","梨子"]


    // 2. reduce循環(huán)
    let oList = listdata.reduce((prev,value) => {
        // 沒事將數(shù)據(jù)包裹react元素后在push到數(shù)組中
        prev.push(<li>{ value }</li>)
        
        // 返回數(shù)組
        return prev
    },[])
  
    return  <ul> { oList } </ul>

}

// 將組件渲染到頁面上
ReactDOM.render(<MyCom /> ,document.getElementById("root"))


比較常用的方法是: map和reduce兩種方法來渲染列表數(shù)據(jù)


2. 列表渲染的key屬性

2.1 可以屬性的認識
  1. 在列表渲染中使用key來標記每一項,讓React能識別每一個元素,

  2. 這樣如果遇到添加或刪除,會很有幫助,key屬性要這個元素成為唯一

  3. key屬性的值最好是這個元素列表中獨一無二的字符串,通常我們用id來 作為元素的key

  4. 當元素沒有id時,萬不得已的情況下 可以使用index索引來作為key,通常不建議,因為這會導致性能的下降

  5. 如果你不指定可以,React將默認用索引index作為key,并在控制臺提醒添加key屬性

注意:

  1. key只在兄弟節(jié)點之間必須唯一
  2. key屬性值會傳遞給React,不會傳遞給組件,如果需要 傳遞給組件,請使用其他屬性傳遞


2.2 使用key屬性渲染列表數(shù)據(jù)

// 1. 列表數(shù)據(jù)
let names = ["HTML,CSS","JavaScript","Node","Vue","React"]

// 2. 創(chuàng)建虛擬DOM
const ul = (
    <ul>
        { names.map( (item,index) => <li key={index}>{item}</li>) }
    </ul>
)

// 3. 渲染虛擬DOM
ReactDOM.render(ul, document.getElementById('list'))


3. 受控組件與非受控組件

3.1 受控組件與非受控制組件說明
  1. 受控組件是指可以被rect狀態(tài)控制的組件
  2. 非受控組件是可以任意輸入內(nèi)容,不受組件狀態(tài)的控制,默認是就是非受控組件


3.2 非受控組件

說明:

  1. 非受控組件不受react狀態(tài)影響
  2. 如果需要獲取非受控組件的數(shù)據(jù),使用ref屬性


示例代碼:

class MyCom extends React.Component{
    // 組件狀態(tài)
    state = {
        username:"",
        password:""
    }

handleClick = () =>{
    // console.log(this);
    
    // 獲取數(shù)據(jù)
    let username = this.userInput.value.trim();
    let password = this.passwordInput.value.trim();

    // 判斷輸入是否為空
    if(!username || !password){
        console.log("用戶名或者密碼不能為空")
        return;
    }

    // console.log(username,":",password)

    // 將數(shù)據(jù)添加到狀態(tài)中
    this.setState(() => ({
        username,password
    }), () => {
        console.log("數(shù)據(jù)已經(jīng)添加到狀態(tài)中");
    })

}

render(){
    return (
        <div>
            <h2>非受控組件獲取數(shù)據(jù)</h2>
            <p>
                用戶名: 
                <input 
                    type="text"  
                    ref={input => this.userInput = input}
                    />
            </p>
            <p>
                密  碼: 
                <input type="password" 
                    ref={input => this.passwordInput = input}
                    />
            </p>
            <button onClick={ this.handleClick }>點擊</button>
        </div>
    )
}
}


// 將組件渲染到頁面上
ReactDOM.render(<MyCom /> ,document.getElementById("root"))


3.3 受控組件

說明:

  1. 受控組件是指將表單綁定到狀態(tài)上,
  2. 狀態(tài)的值一但發(fā)生變化,輸入空的內(nèi)容也會發(fā)生變化,
  3. 狀態(tài)的值不變,輸入框不能輸入任何內(nèi)容
  4. 也就是說輸入框中的內(nèi)容被狀態(tài)所控制


示例代碼如下:

class MyCom extends React.Component{
    // 組件狀態(tài)
    state = {
        username:"",
        password:""
    }

userChangeHandle = (event) =>{

    // // 獲取用戶名數(shù)據(jù)
    let username = event.target.value.trim();

    // 判斷輸入是否為空
    if(!username){
        console.log("用戶名不能為空")
        return;
    }

    // console.log(username,":",password)

    // 將數(shù)據(jù)添加到狀態(tài)中
    this.setState(() => ({
        username
    }), () => {
        console.log("用戶名數(shù)據(jù)已經(jīng)添加到狀態(tài)中");
    })

}

pwChangeHandle = (event) =>{
    // console.log(this);
    // // 獲取密碼數(shù)據(jù)
    let password = event.target.value.trim();

    // 判斷輸入是否為空
    if(!password){
        console.log("密碼不能為空")
        return;
    }

    // console.log(username,":",password)

    // 將數(shù)據(jù)添加到狀態(tài)中
    this.setState(() => ({
        password
    }), () => {
        console.log("密碼數(shù)據(jù)已經(jīng)添加到狀態(tài)中");
    })

}

render(){
    // 1. 獲取狀態(tài)中的數(shù)據(jù)
    let {username, password} = this.state

    // 2. 將狀態(tài)中的數(shù)據(jù)綁定到輸入框,此時輸入框就是受控組件
    return (
        <div>
            <h2>非受控組件獲取數(shù)據(jù)</h2>
            <p>
                用戶名: 
                <input 
                    type="text" 
                    value={username}
                    onChange = { this.userChangeHandle }
                    />
            </p>
            <p>
                密  碼: 
                <input type="password" 
                    value={password}
                    onChange = { this.pwChangeHandle }
                    />
            </p>
            <button onClick={ this.handleClick }>點擊</button>
        </div>
    )
}
}


// 將組件渲染到頁面上
ReactDOM.render(<MyCom /> ,document.getElementById("root"))
  1. 示例中標簽輸入內(nèi)容沒有任何限制,不被狀態(tài)控制,
  2. 在通過ref

示例說明:

  1. 輸入框的value值綁定到了狀態(tài)中的數(shù)據(jù),因此輸入框就被狀態(tài)控制
  2. 此時輸入框不能輸入任何內(nèi)容, 因為是狀態(tài)單向影響輸入框,而且此時還會報錯
  3. 如果我們希望能在輸入框輸入的內(nèi)容,就需要綁定onChange事件
  4. 當輸入內(nèi)容時,通過onChange綁定的事件函數(shù)獲取輸入的內(nèi)容,修改狀態(tài)值
  5. 狀態(tài)值一但發(fā)生變化,表單就會顯示最新的數(shù)據(jù). 看起來好像可以輸入了


4. 收集表單數(shù)據(jù)

4.1 收集標簽數(shù)據(jù)說明:
  1. 受控制和非受控組件都可以收集表單數(shù)據(jù)

  2. 但是非受控組件是在操作DOM,

  3. 受控組件不是在操作DOM, 按照react思想減少DOM操控,所以官網(wǎng)推薦受控組件


4.2 收集標簽數(shù)據(jù)代碼

使用受控組件收集標簽數(shù)據(jù)

需求

  1. 收集用戶輸入的用戶名,密碼
  2. 收集性別愛好,單選復選數(shù)據(jù)
  3. 收集select數(shù)據(jù)


示例代碼如下

class MyCom extends React.Component{
    // 組件狀態(tài)
    state = {
        username:"",
        password:"",
        sex:"0",  // 0為男,1為女
        likes:[],
        province:""
    }

// 處理用戶名
userChangeHandle = (event) =>{

    // // 獲取用戶名數(shù)據(jù)
    let username = event.target.value.trim();

    // 判斷輸入是否為空
    if(!username){
        console.log("用戶名不能為空")
        return;
    }

    // console.log(username,":",password)

    // 將數(shù)據(jù)添加到狀態(tài)中
    this.setState(() => ({
        username
    }))

}

// 處理密碼
pwChangeHandle = (event) =>{
    // console.log(this);
    // // 獲取密碼數(shù)據(jù)
    let password = event.target.value.trim();

    // 判斷輸入是否為空
    if(!password){
        console.log("密碼不能為空")
        return;
    }

    // console.log(username,":",password)

    // 將數(shù)據(jù)添加到狀態(tài)中
    this.setState(() => ({
        password
    }))
}

// 處理性別 單選
changeSex = (event) => {
    let sex = event.target.value;

    this.setState(() => ({
        sex
    }))

}

// 處理愛好  多選
changeLike = (event) => {
    // 獲取復選框改變的數(shù)據(jù)
    let like = event.target.value;

    // 獲取狀態(tài)中收集愛好的數(shù)據(jù)
    let {likes} = this.state

    // 判斷狀態(tài)中是否存在,存在就刪除,不存在就添加
    if(!likes.includes(like)){
        likes.push(like)
    }else{
        likes = likes.filter(value => {
            return like != value
        })
    }

    // 更新數(shù)據(jù)
    this.setState(() => ({
        likes
    }))
}

// 獲取省份數(shù)據(jù)
changeProvince = (event) => {
    let province = event.target.value;
    this.setState(() => ({
        province
    }))
}

render(){
    // 1. 獲取狀態(tài)中的數(shù)據(jù)
    let {
        username, 
        password,
        sex,
        likes,
        province
    } = this.state

    // 2. 將狀態(tài)中的數(shù)據(jù)綁定到輸入框,此時輸入框就是受控組件
    return (
        <div>
            <h2>非受控組件獲取數(shù)據(jù)</h2>
            <p>
                用戶名: 
                <input 
                    type="text" 
                    value={username}
                    onChange = { this.userChangeHandle }
                    />
            </p>
            <p>
                密  碼: 
                <input type="password" 
                    value={password}
                    onChange = { this.pwChangeHandle }
                    />
            </p>
            <p>
                性別: 
                <label htmlFor="male">
                    <input 
                        id="male"
                        type="radio" 
                        value="0"
                        checked={sex == 0 }
                        onChange = { this.changeSex }
                        />
                    男
                </label>
                <label htmlFor="female">
                    <input 
                        id="female"
                        type="radio" 
                        value="1"
                        checked={sex == 1 }
                        onChange = { this.changeSex }
                        />
                    女
                </label>
            </p>
            <p>
                愛好: 
                <label htmlFor="like1">
                    <input 
                        id="like1"
                        type="checkbox" 
                        value="游泳"
                        checked={ likes.some(like => like == "游泳") }
                        onChange = { this.changeLike }
                        />
                    游泳
                </label>
                <label htmlFor="like2">
                    <input 
                        id="like2"
                        type="checkbox" 
                        value="籃球"
                        checked={ likes.some(like => like == "籃球") }
                        onChange = { this.changeLike }
                        />
                    籃球
                </label>
                <label htmlFor="like3">
                    <input 
                        id="like3"
                        type="checkbox" 
                        value="足球"
                        checked={ likes.some(like => like == "足球") }
                        onChange = { this.changeLike }
                        />
                    足球
                </label>
            </p>
            <p>
                省份: 
                <select 
                    value={province} 
                    onChange={this.changeProvince }
                    >
                    <option value="">請選擇</option>
                    <option value="北京">北京</option>
                    <option value="上海">上海</option>
                    <option value="廣東">廣東</option>
                    <option value="安徽">安徽</option>
                    <option value="湖南">湖南</option>
                </select>
            </p>
            <button onClick={ this.handleClick }>點擊</button>
        </div>
    )
}
}


// 將組件渲染到頁面上
ReactDOM.render(<MyCom /> ,document.getElementById("root"))

示例說明:

  1. 用戶名,密碼表單受控組件直接綁定value值
  2. 單選復選通過checked判斷是否選擇中,因此將狀態(tài)中用來數(shù)據(jù)對應數(shù)據(jù)字段的數(shù)據(jù)跟value對比
  3. select受控組件收集數(shù)據(jù),將value綁定在select標簽上


5. 虛擬DOM與DOM Diff算法

01.png

初始化顯示界面

  1. 創(chuàng)建虛擬DOM樹(一般JS對象)
  2. 真實DOM樹
  3. 將虛擬DOM添加真實DOM繪制界面顯示

初始化看不出虛擬DOM的優(yōu)勢,優(yōu)勢主要體現(xiàn)在更新,因為頁面大部分情況下都是在更新

更新界面

  1. setState() 更新狀態(tài)
  2. 重新創(chuàng)建虛擬DOM樹
  3. 新/舊樹比較差異
  4. 更新差異對應的真實DOM
  5. 局部界面重繪

為什么狀態(tài)不能直接就改,而是要通過setState,因為直接修改沒法比較,setState會重新創(chuàng)建新狀態(tài),生成新的虛擬DOM,可以與原來狀態(tài)和虛擬DOM進行對比

例子演示;


// 1. 定義組件
class Hello extends React.Component {

    constructor (){
        super()

        // 定義狀態(tài)
        this.state = {
            date: new Date()
        }

    }


    componentWillMount(){
        setInterval(()=>{
            this.setState({
                date: new Date()
            })
        },1000)
    }


    render(){
        console.log('render()')
        let {date} = this.state;

        return (
            <div>
                輸入內(nèi)容: <input type="text" /> <span>{date.toTimeString()}</span>
            </div> 
        )
    }
}



// 2. 渲染組件標簽
ReactDOM.render(
    <Hello />, 
    document.getElementById('box')
)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容