Easy的React學習筆記(一.基礎)

目錄

1_開發環境搭建

2_用命令create-react-app (腳手架)創建項目目錄demo1

3_通過組件化開發輸出helloworld

4_JSX語法

5_小姐姐服務頁面(Xiaojiejie.js)編寫

6_React響應式設計原理與數據的綁定方法、增加服務項

需求Z:

this.state:react中用來控制組件內部狀態的數據

onchange 事件:

7_增加服務項

①讓列表數據化

②增加服務項

8_刪除服務項

9_jsx中幾個要注意的地方:代碼註釋、css的class陷阱、html解析問題dangerouslySetInnerHTML屬性、label標籤的for

1.代碼註釋:

2.jsx中css的class陷阱,應爲className

3.html解析問題的:dangerouslySetInnerHTML屬性

4.label標籤的htmlFor

10_react快速生成代碼插件:Simple React Snippets

11_組件拆分

12_父子組件相互傳值

1.父組件向子組件傳值:在子組件加上屬性的形式。

2.子組件操作父組件數據(實際上是父組件向子組件傳方法):在子組件加上屬性的形式。

父組件向子組件傳方法:

①父組件向子組件傳值 、子組件向父組件傳值(父組件向子組件傳方法)都是在子組件加上屬性的形式實現的,然後通過this.props.屬性名的方式訪問。

this.props:

React中,使用外部(被傳入傳入的)數據(通過 this.props 訪問),被傳入的數據可在組件中通過 this.props 在 render() 訪問。

註釋:(props:屬性properties)

this.state:

組件還可以維護其內部的有狀態的數據(通過 this.state 訪問)。

註釋:(state :狀態;有狀態的數據)

②調用方法時要綁定this指向,可以直接在調用處bind綁定,但是更好的是在組件構造函數內綁定,這樣有助於性能優化。

13_React的單項數據流、函數式編程

1.單項數據流

2.函數式編程

14_react developer tools的安裝與使用

1.安裝詳見我另一篇博客:

2.react developer tools插件的三種狀態

3.使用

由於本學習筆記過長,圖片較多,編輯器打字時有些卡頓,所以之後內容寫在另一篇博客:


1_開發環境搭建

①前往http://nodejs.cn/,找到自己電腦適合的版本下載並安裝node.js。

②win鍵+r(運行),cmd打開命令行。

輸入相關命令檢查node.js是否安裝成功,顯示nodejs與npm對應的版本即爲安裝成功。

安裝node主要爲了使用npm包管理工具。

③安裝官方腳手架工具create-react-app。

若無法安裝create-react-app(安裝失敗),

可嘗試更換網絡環境後重新安裝,若還是失敗,排除網絡因素後,則以管理員身份運行cmd(命令提示符)後成功安裝create-react-app

看到安裝了91個包就表示安裝成功。

2_用命令create-react-app (腳手架)創建項目目錄demo1

創建失敗:

解決方法:

同樣以管理員身份運行cmd(命令提示符),重新輸入命令create-react-app 創建項目demo1

成功用create-react-app創建demo1,可以看到demo1裏面有許多腳手架生成的文件。

根據命令提示運行我的第一個react文件

3_通過組件化開發輸出helloworld

把src目錄裏面的文件全部刪掉,然後新建index.js(入口文件)App.js(方法組件,React核心之一就是組件化開發)

入口文件index.js代碼如下(用ReactDOMrender渲染語法把APP模塊渲染到public\index.html中的root上):

import React from 'react'  //要用react所以引入react
import ReactDOM from 'react-dom' //要操作dom所以引入react-dom
import App from './App' //組件化開發,有了App組件後就可以通過reactdom的render方法在public\index.html的rootID上渲染出來

ReactDOM.render(<App />,document.getElementById('root'))  //用react語法把App組件render到index.html中id爲root的標籤上

注意此時index.js中的三句導入都是導入默認組件,React、ReactDOM、App這三個組件名都是自己取得,取ABC也可以,但是爲了做到望文生義,以固定寫法比較好。其中React、ReactDOM是導入默認組件,App是導入的自定義組件(必須以大寫字母開頭

組件App.js代碼如下(React中自定義組件名必須以大寫字母開頭,例如本例的App組件必須大寫字母開頭,不然會報錯):

import React,{Component} from "react"
// import React from 'react'
// Const Component = React.Component即import {Component} from 'react'
// 這裏是es6解構賦值的寫法,即Component等於React.Component
class App extends Component{
    render(){
        return (
            <div>
                 Hello,EasyLee
                <ul className='my_list'>
                    <li>{true ? 'i love coding' : 'i love eating' }</li>
                    <li>i love react</li>
                </ul>
            {/* 上述jsx代碼相當於如下js代碼:
             var child1 = React.createElement('li',null,'i love coding');
             var chidl2 = React.createElement('li',null,'i love react');
             var chidl3 = React.createElement('ul',{className='my_list'},child1,child2); */}
            </div>
            
        )
    }
}
export default App;//在index.js文件中要引用App組件所以最後要用export default暴露(輸出)App組件。

在index.js文件中要引用App組件所以最後要用export default暴露(輸出)App組件。

疑問:可以把import React,{Component} from 'react'中的Component改成a嗎?

答:不能,Component是react的成員組件,react官方自己定義好的,所以不能鎖邊取一個名字。

public目錄下模板文件index.html代碼如下:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <link rel="apple-touch-icon" href="logo192.png" />
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    <title>EasyLee WEB</title>
  </head>
  <body>
    <noscript>需要開啓JavaScript.</noscript>
    <div id="root"></div>
  </body>
</html>

有幾點(①②③④)要注意:

①入口文件index.js與組件App.js導入的頭部文件不同,:

入口文件index.js頭部通常由三部分組成:導入react默認組件、導入ReactDOM、導入自定義組件。

②入口文件index.js與組件App.js導入的底部代碼不同:

入口文件index.js下方把自定義組件(以App爲例)通過ReactDOM.render渲染到index.html文件上。

自定義組件App.js底部要通過export default 語句導出。

index.js(入口文件):

import React from "react"//①要用react所以引入react
import ReactDOM from "react-dom"//②要操作dom所以引入react-dom
import App from "./App"//③導入自定義組件

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

App.js(自定義組件以App.js爲例,底部要通過export default 語句導出

import React,{Component} from "react"
// import React from 'react'
// Const Component = React.Component即import {Component} from 'react'
// 這裏是es6解構賦值的寫法,即Component等於React.Component
export default App;//底部導出自定義組件

③自定義組件名首字母必須大寫,如App。

④牢記自定義組件的語法結構,以App.js組件爲例:

import React,{Component} from "react"
class App extends Component{
    render(){
        return (
            <div>

            </div>        
        )
    }
}
export default App;//底部導出自定義組件

 

4_JSX語法

JSX就是Javascript和XML結合的一種格式。React發明了JSX,可以方便的利用HTML語法來創建虛擬DOM,當遇到<,JSX就當作HTML解析,遇到{就當JavaScript解析.

import React,{Component} from 'react'
// import React from 'react'
// Const Component = React.Component即import {Component} from 'react'
// 這裏是es6解構賦值的寫法,即Component等於React.Component
class App extends Component{
    render(){
        return (
            <div>
                {/* Hello,EasyLee! */}
                <ul className='my_list'>
                    <li>{true ? 'i love coding' : 'i love eating' }</li>
                    <li>i love react</li>
                </ul>
            </div>
            
        )
    // 上述jsx代碼相當於如下js代碼:
    //         var child1 = React.createElement('li',null,'i love coding');
    //         var chidl2 = React.createElement('li',null,'i love react');
    //         var chidl3 = React.createElement('ul',{className='my_list'},child1,child2);
    }
}
export default App;

其中 createElement(a, b, c)方法用法如下:

①參數 a:表示元素的類型,比如:h1, div 等。

②參數 b:表示該元素上的屬性,使用 JavaScript 對象方式表示。

③參數 c:表示該元素內部的內容(子元素),可以是文字,可以繼續嵌套另外一個 React.createElement(a, b, c)

這種方法其實在實際 React 開發中幾乎不會使用,因爲可以直接用JSX語法 。

5_小姐姐服務頁面(Xiaojiejie.js)編寫

疑問:爲何每個自定義組件都要引入React?

答:因爲webpack在解析jsx的時候會把下面代碼的Component解析成React.Component這個方法,假如他發現沒有引入React,就會報錯。

Xiaojiejie.js代碼如下:

import React,{Component} from 'react'
class Xiaojiejie extends Component{
    render(){
        return (
        <div>   
            <input /><button>增加服務</button>
            <ul>
                <li>服務1</li>
                <li>服務2</li>
            </ul>   
        </div> 
        )

    }
}
export default Xiaojiejie;

在index.js中加入如下代碼:

ReactDOM.render(<Xiaojiejie />,document.getElementById('xiaojiejie'))

在index.html中加入如下代碼:

    <div id="xiaojiejie"></div>

加入Xiaojiejie.js後運行效果如圖所示:

問:由於React要求必須在一個組件的最外層進行包裹,如下圖的光標處的div,不加就會報錯,但是在Flex佈局中,return內最外層包裹div會影響佈局,Flex佈局中最外層不能有包裹,這時候怎麼辦呢?

答:Flex佈局中<div>由<Fragment>替代,不過記得要在頭部多引入react的成員組件Fragment(注意成員組件Fragment的首字母是大寫的,引入和使用Fragment標籤時要注意):

import React,{Component,Fragment } from 'react'
class Xiaojiejie extends Component{
    render(){
        return(
            <Fragment>  {/*注意Fragment標籤首字母大寫*/}
               <div>
                <input />
                <button>增加服務</button>
                </div>
                <ul>
                    <li>服務1</li>
                    <li>服務2</li>
                </ul>
            </Fragment>
        )
    }
}

6_React響應式設計原理與數據的綁定方法、增加服務項

React如此受歡迎的原因(React響應式設計原理):

React不建議直接操作dom,而是讓你通過數據的改變從而自動幫你完成界面的改變,所以程序員無需關心dom操作只關心數據操作即可,大大加快了開發速度。

需求Z

點擊增加服務按鈕就能在下方列表自動增加小姐姐的服務項(爲了實現需求Z,需求Z又拆分爲步驟ABCD,在小節6_中還沒完全實現需求Z,待小節7_中完善)。

實現Z

{步驟A(分爲①②):在Xiaojiejie組件中添加構造函數,然後在構造函數constructor內定義數據。 

①構造函數constructor(一般是固定寫法必須背下來, =:等標點符號要注意別寫錯):

    constructor(props){
        super(props)//調用父類方法(固定寫法)
        this.state={
            inputValue : '233',//input裏的值,注意符號是:不是=,因爲inputValue、list都是this.state的屬性,屬性間用逗號隔開
            list:[]//服務列表
        }
    }

this.state:react中用來控制組件內部狀態的數據

②數據綁定(React中的數據綁定採用字面量{ }的形式,上文提到jsx就是遇到{ }當js代碼解析、遇到<>當html解析,{ }其實就是在jsx中使用js代碼時做的聲明):

 <input value={this.state.inputValue} /> 

需求A結束}

 

由於我們在①中強行將inputValue 的數據綁定爲233是寫死的,所以在文本框中輸入任何東西都是沒反應的,如圖所示:

而且打開控制檯發現報錯,而且鍵盤輸入控制檯都沒反應,因爲已經報錯了:

控制檯提示:錯誤可設置onChange屬性,這個錯誤可以設置onChange值使控制檯恢復正常。

{需求B:改正控制檯的報錯

需求B的實現:爲input標籤設置onChange事件,綁定響應事件inputChange解決控制檯報錯點擊查看onChange事件用法

<input value={this.state.inputValue} onChange={this.inputChange} /> {/* input的onChange屬性:狀態值變化 */} 

爲input標籤嘉善onChange事件後,控制檯就不報錯了。

onchange 事件:

①定義和用法:onchange 事件會在域的內容改變時發生

②語法:οnchange="SomeJavaScriptCode"

需求B結束}

 

{需求C:鍵盤輸入能改變inputValue的值(改變文本框中的數據),而不是寫死的233佔據文本框的位置

需求C的實現(分爲步驟C1.C2):

步驟C1:首先讓鍵盤輸入能輸出到控制檯中,

在render函數下方,加入響應事件inputChange(),並把onChange屬性綁定新寫的響應事件inputChange(),讓控制檯能輸出數據

    inputChange(e){     //綁定響應事件改變文本框inputValue的值
        console.log(e);
    }

此時在文本框中輸入時控制檯就有反應了,在控制檯中會輸出很多內容了,這些內容就是e,如下面兩張圖所示:

此時響應事件能用了,那如何獲取從鍵盤輸入的值?

{步驟C2:通過e.target.value獲取從鍵盤輸入的值(注意e.target.value就是inputValue的值,即e.target.value等於寫死的233+鍵盤鍵入值)

    inputChange(e){     //綁定響應事件改變文本框inputValue的值
        console.log(e.target.value);//15_通過e.target.value打印文本框內的值
    }

此時控制檯打印出文本框的值,e.target.value就是233加上鍵入的值(如圖所示):

此時想當然的會認爲只要將e.target.value的值賦給this.state.inputValue就完事了,就能去掉寫死的數據233了,但是這樣寫是錯誤的,控制檯報錯:

    inputChange(e){     //綁定響應事件改變文本框inputValue的值
        console.log(e.target.value);//15_通過e.target.value打印文本框內的值
        this.state.inputValue=e.target.value;//錯誤寫法:錯誤①this指向錯誤②沒用this.setState方法
    }

this.state.inputValue=e.target.value這種寫法犯了兩個錯誤(①②):

①this指向不正確,所以用bind重新綁定指向(ES5語法):

<input value={this.state.inputValue} onChange={this.inputChange.bind(this)} /> {/* input的onChange屬性:狀態值變化;bind重新綁定this指向*/} 

參考資料:

MDN,函數的 this 關鍵字

MDN,bind() 方法

②React中,改變組件內部的狀態數據的值不能想當然的 this.state.inputValue=e.target.value(錯誤寫法這樣寫,需要用到this.setState()方法:

inputChange(e){     //綁定響應事件改變文本框inputValue的值
    // console.log(e);
    // console.log(e.target.value);//15_通過e.target.value打印文本框內的值
    // this.state.inputValue=e.target.value;//錯誤寫法:錯誤①this指向錯誤②沒用this.setState方法
    this.setState({      //React中改變組件內部的狀態數據的值要用this.setState方法,注意setState的的大小寫
        inputValue:e.target.value
    })
}

此時在原本固定的inputValue爲233的文本框中輸入可改變文字了,如圖所示:

步驟C2結束}

步驟C結束}

7_增加服務項

{需求D:完善構造函數,讓render中寫死的列表數據化、增加服務項

需求D的實現(①②):

①讓列表數據化

先在構造函數內爲list數組增加兩個數組元素,代碼如下:

 constructor(props){
        super(props)//調用父類方法(固定寫法)
        this.state = {
        inputValue:'233',//input裏的值,注意符號是:不是=,因爲inputValue、list都是this.state的屬性,屬性間用逗號隔開
        list:['服務1','服務2'] //服務列表,記得加單引號,不然報錯了
        }
}

構造函數中有了數據之後,用JavaScript Array map() 方法箭頭函數循環輸出列表數據,代碼如下:

render(){
    return(
        <Fragment> {/*注意Fragment標籤首字母大寫*/}
            <div>
            <input value={this.state.inputValue} onChange={this.inputChange.bind(this)} /> {/* input的onChange屬性:狀態值變化;bind重新綁定this指向*/} 
            <button>增加服務</button>
            </div>
            <ul>
                {/* <li>服務1</li>
                    <li>服務2</li> */}
                {/* 技術胖寫法 */}
                {
                this.state.list.map((item,index)=>{
                    return <li>{item}</li>
                })
                } 
                {/* {
                    this.state.list.map(
                        (item)=>{return <li key={item}>{item}</li>} 
                    )
                } */}
            </ul>
        </Fragment>
    )
}

箭頭函數中有兩個參數:

一.item是循環的每一個子項

二.index是索引

服務列表動態循環輸出了,實現了列表數據化,但是發現控制檯報錯(一會解決):

報錯意思是list要有一個key值,暫用index+item來實現:

<ul>
    {/* 原本寫死的列表數 */}
    {/* <li>服務1</li>
        <li>服務2</li> */}
    {/* Easy寫法 */}
    {/* {
        this.state.list.map(
            (item)=>{return <li key={item}>{item}</li>} 
        )
    } */}                        
    {/* 技術胖寫法 */}
    {
    this.state.list.map((item,index)=>{
        return <li key={item+index}>{item}</li>
    })
    } 
</ul>

報錯解決。

點擊:查看JavaScript Array map方法(es6)、箭頭函數用法。

一個好的經驗法則是:在 map() 方法中的元素需要設置 key 屬性(https://zh-hans.reactjs.org/docs/lists-and-keys.html#gatsby-focus-wrapper)

疑問:爲什麼map方法內第一個參數就是當前元素的值,第二個參數就是索引呢?

答:詳見Array.map方法的用法

②增加服務項

在增加按鈕上先綁定一個新寫的addList方法,

<button onClick={this.addList.bind(this)}>增加服務</button>

在之前的inputChange方法後新寫addList方法:

    addList(){        //爲按鈕綁定增加服務項事件
        this.setState({
            list:[...this.state.list,this.state.inputValue]
        })
    }

...這個是ES6的新語法,名爲擴展運算符。作用把list數組進行了分解,形成了新的數組,然後再進行組合。這種寫法更簡單和直觀,所以推薦這種寫法。

點擊添加服務按鈕可以實現添加文本框中的數據到服務列表了。

理論上每次增加服務項之後,文本框應該清空,只需要在list下面加上如下代碼即可:

inputValue:''

addList方法最終代碼:

addList(){      //爲按鈕綁定增加服務項事件
    this.setState({
        list:[...this.state.list,this.state.inputValue], //...這個是ES6的新語法,名爲擴展運算符。作用把list數組進行了分解,形成了新的數組,然後再進行組合。這種寫法更簡單和直觀,所以推薦這種寫法。
        inputValue:''
    })
}

需求D結束}

至此小節6_中的需求Z通過ABCD成功實現。

8_刪除服務項

需求:鼠標點擊服務項,自動刪除。

實現:

要刪除一個東西,就先獲取數組下標下標。然後爲列表添加點擊事件,併爲點擊事件綁定刪除方法deleteItem。

爲點擊事件綁定刪除方法deleteItem有兩種方法:普通方法、箭頭函數方法,區別就在於是否需要綁定this,如下代碼所示:

//return加了括號,下面的jsx就可以換行寫,看的更清楚

<ul>
    {/* 原本寫死的列表數 */}
    {/* <li>服務1</li>
        <li>服務2</li> */}
    {/* Easy寫法 */}
    {/* {
        this.state.list.map(
            (item)=>{return <li key={item}>{item}</li>} 
        )
    }                      */}
    {/* 技術胖寫法 */}
    { 
    this.state.list.map((item,index)=>{
        return( //return加了括號,下面的jsx就可以換行寫,看的更清楚
            //普通寫法 
            <li key={item+index} onClick={this.deleteItem.bind(this,index)}>   
                {item}
            </li>   
            //  箭頭函數寫法不用綁定this,因爲箭頭函數不會創建自己的this,它只會從自己的作用域鏈的上一層繼承this。 
            // <li key={item+index} onClick={()=>this.deleteItem(index)}>    
            //     {item}
            // </li>
        )   
    })
    } 
</ul>

 

deleteItem(index){ //刪除列表服務事件
    // console.log(index)//測試點擊列表項能否取得相應下標
    // this.state.list.splice(index,1)錯誤寫法
    // this.setState({
    //     list:this.state.list
    // }) 錯誤寫法,即便也能和下面代碼實現看似一樣的效果,但是react中禁止直接修改state的值,後期優化會有性能問題
    let list = this.state.list
    list.splice(index,1)
    this.setState({
        list:list
    })
}

9_jsx中幾個要注意的地方:代碼註釋、css的class陷阱、html解析問題dangerouslySetInnerHTML屬性、label標籤的for

1.代碼註釋:

vscode中代碼註釋快捷鍵:ctrl+/

詳見本人的另一篇博客:

jsx代碼註釋格式規範最全總結

————————————————
版權聲明:本文爲CSDN博主「Easy_Lee_willpower」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/qq_37279880/article/details/105885633

2.jsx中css的class陷阱,應爲className

在src文件中新建style.css文件,:

 style.css中的代碼:

.input {
    border:3px solid #00FF00;
}

在Xiaojiejie.js頭部導入該文件:

import './style.css'

在input標籤上加上class屬性(這種寫法是錯誤的):

<input class='input' value={this.state.inputValue} onChange={this.inputChange.bind(this)} /> {/* input的onChange屬性:狀態值變化;bind重新綁定this指向*/} 

 warning警告,爲了防止css的class與js中的class衝突,所以要把input中的class屬性改成className:

input標籤修改後:

<input className='input' value={this.state.inputValue} onChange={this.inputChange.bind(this)} /> {/* input的onChange屬性:狀態值變化;bind重新綁定this指向*/} 

3.html解析問題的:dangerouslySetInnerHTML屬性

想在文本框輸入<h2>標籤後,在按下增加服務後能直接渲染出來,而不是如下圖效果:

通過dangerouslySetInnerHTML 屬性實現:

在li中加入dangerouslySetInnerHTML 屬性,代碼如下:

<li 
key={item+index} 
nClick={this.deleteItem.bind(this,index)} 
dangerouslySetInnerHTML={{__html:item}}
>  
</li>   

4.label標籤的htmlFor

需求:點擊增加服務就能激活文本框

實現代碼如下:

<label for='addList'>增加服務</label>
<input id='addList' className='input' value={this.state.inputValue} onChange={this.inputChange.bind(this)} /> 

 控制檯警告:

此時把label的for改成htmlFor即可,代碼如下:

<label htmlFor='addList'>增加服務</label>
<input id='addList' className='input' value={this.state.inputValue} onChange={this.inputChange.bind(this)} /> {/* input的onChange屬性:狀態值變化;bind重新綁定this指向*/} 

原因:怕DOM操作的htmlFor與js的for循環混淆。

10_react快速生成代碼插件:Simple React Snippets

安裝步驟:

1.點擊擴展插件

2.搜索Simple React Snippets

3.安裝install

 安裝後就可通過對應的Snippets(片段)快速生成(渲染render)大段代碼:

11_組件拆分

需求:實際開發中,會把不同功能的組件拆分開寫,本項目把服務列表組件拆分。

實現:

在src目錄下新建Xiaojiejie組件的子組件XiaojiejieItem.js代碼如下:

import React, { Component } from 'react';
class XiaojiejieItem extends Component {
    render() { 
        return ( 
            <li>子組件</li>
            // <li>{this.props.content}</li>
        );
    }
}
 
export default XiaojiejieItem;

在Xiaojiejie.js頭部引入子XiaojiejieItem組件:

import XiaojiejieItem from './XiaojiejieItem'

並修改ul內代碼,Xiaojiejie.js代碼如下:

import React,{Component,Fragment} from "react"
import './style.css'
import XiaojiejieItem from './XiaojiejieItem'

class Xiaojiejie extends Component{
    constructor(props){
        super(props)//調用父類方法(固定寫法)
        this.state = {
            inputValue:'',//input裏的值,注意符號是:不是=,因爲inputValue、list都是this.state的屬性,屬性間用逗號隔開
            list:['服務1','服務2'] //服務列表,記得加單引號,不然報錯了
        }
    }
    render(){
        return(
            <Fragment> {/*注意Fragment標籤首字母大寫*/}
                <div>
                    <label htmlFor='addList'>增加服務</label>
                    <input id='addList' className='input' value={this.state.inputValue} onChange={this.inputChange.bind(this)} /> {/* input的onChange屬性:狀態值變化;bind重新綁定this指向Xiaojiejie*/} 
                    <button onClick={this.addList.bind(this)}>增加服務</button>
                </div>
                <ul>
                {/* 原本寫死的列表數 */}
                {/* <li>服務1</li>
                    <li>服務2</li> */}
                {/* Easy寫法 */}
                {/* {
                    this.state.list.map(
                        (item)=>{return <li key={item}>{item}</li>} 
                    )
                    }                      
                */}
                {/* 技術胖寫法 */}
                { 
                    this.state.list.map((item,index)=>{
                        return(                           
                        // return加了括號,下面的jsx就可以換行寫,看的更清楚
                        //     普通寫法                 
                        //     <li 
                        //     key={item+index} 
                        //     onClick={this.deleteItem.bind(this,index)} 
                        //     dangerouslySetInnerHTML={{__html:item}}
                        //     >  
                        //     </li>   
                        //     箭頭函數寫法:不用綁定this,因爲箭頭函數不會創建自己的this,它只會從自己的作用域鏈的上一層繼承this。 
                        //     <li key={item+index} onClick={()=>this.deleteItem(index)}>    
                        //         {item}
                        //     </li>                              
                        //      拆分成子組件組件寫法
                                <XiaojiejieItem />

                        )   
                    })
                } 
                </ul>
            </Fragment>
        )
    }
    inputChange(e){     //綁定響應事件改變文本框inputValue的值
        // console.log(this);
        // console.log(e);//e是一堆內容
        // console.log(e.target.value);//15_通過e.target.value打印文本框內的值
        // this.state.inputValue=e.target.value;//錯誤寫法:因爲沒用沒用this.setState方法
        // console.log(this);//輸出undefined,所以要bind方法綁定this
        // console.log(this === window);//false
        this.setState({      //React中改變組件內部的狀態數據的值要用this.setState方法,注意setState的的大小寫
            inputValue:e.target.value
        })
    }
    addList(){      //爲按鈕綁定增加服務項事件
        this.setState({
            list:[...this.state.list,this.state.inputValue], //...這個是ES6的新語法的擴展運算符,名爲擴展運算符。作用把list數組進行了分解,形成了新的數組,然後再進行組合。這種寫法更簡單和直觀,所以推薦這種寫法。
            inputValue:''
        })
    }
    deleteItem(index){ //刪除列表服務事件
        // console.log(index)//測試點擊列表項能否取得相應下標
        // this.state.list.splice(index,1)錯誤寫法
        // this.setState({
        //     list:this.state.list
        // }) 錯誤寫法,即便也能和下面代碼實現看似一樣的效果,但是react中禁止直接修改state的值,後期優化會有性能問題
        let list = this.state.list
        list.splice(index,1)
        this.setState({
            list:list
        })
    }
}
export default Xiaojiejie;

此時由於Array.map循環內是子組件XiaojiejieItem,而XiaojiejieItem中的li裏面的代碼又是寫死的,所以頁面如下:

12_父子組件相互傳值父組件向子組件傳值中,解決li數據寫死問題。

12_父子組件相互傳值

1.父組件向子組件傳值:在子組件加上屬性的形式。

步驟一:在Xiaojiejie.js的子組件XiaojiejieItem中加入了content={item}屬性:

<ul>
{/* 原本寫死的列表數 */}
{/* <li>服務1</li>
    <li>服務2</li> */}
{/* Easy寫法 */}
{/* {
    this.state.list.map(
        (item)=>{return <li key={item}>{item}</li>} 
    )
    }                      
*/}
{/* 技術胖寫法 */}
{ 
    this.state.list.map((item,index)=>{
        return(                           
        // return加了括號,下面的jsx就可以換行寫,看的更清楚
        //     普通寫法                 
        //     <li 
        //     key={item+index} 
        //     onClick={this.deleteItem.bind(this,index)} 
        //     dangerouslySetInnerHTML={{__html:item}}
        //     >  
        //     </li>   
        //     箭頭函數寫法:不用綁定this,因爲箭頭函數不會創建自己的this,它只會從自己的作用域鏈的上一層繼承this。 
        //     <li key={item+index} onClick={()=>this.deleteItem(index)}>    
        //         {item}
        //     </li>                              
        //      拆分成子組件組件寫法
                <XiaojiejieItem content={item} />
        )   
    })
} 
</ul>

步驟二:在XiaojiejieItem.js子組件中,通過this.props.屬性名的方式接收父組件傳遞過來的值。

import React, { Component } from 'react';
class XiaojiejieItem extends Component {
    render() { 
        return ( 
            <li>{this.props.content}</li>
        );
    }
}
 
export default XiaojiejieItem;

在父組件Xiaojiejie引用子組件XiaojiejieItem時爲XiaojiejieItem加入了content={item}屬性,又在XiaojiejieItem.js子組件XiaojiejieItem的li中通過this.props.content代碼接收,解決了11_組件拆分中數據寫死的問題。

運行結果(li中數據不再寫死):

 

2.子組件操作父組件數據(實際上是父組件向子組件傳方法):子組件加上屬性的形式。

功能需求:

點擊服務項實現刪除。該功能原本已經實現了,不過現在組件拆分了,就涉及了子組件向父組件傳遞數據,實際上還是靠父組件傳遞方法給子組件,子組件再用父組件的方法來操作父組件的數據。

步驟一:在Xiaojiejie.js的子組件XiaojiejieItem中加入了index、key屬性,代碼如下:

<ul>
{/* 原本寫死的列表數 */}
{/* <li>服務1</li>
    <li>服務2</li> */}
{/* Easy寫法 */}
{/* {
    this.state.list.map(
        (item)=>{return <li key={item}>{item}</li>} 
    )
    }                      
*/}
{/* 技術胖寫法 */}
{ 
    this.state.list.map((item,index)=>{
        return(                           
        // return加了括號,下面的jsx就可以換行寫,看的更清楚
        //     普通寫法                 
        //     <li 
        //     key={item+index} 
        //     onClick={this.deleteItem.bind(this,index)} 
        //     dangerouslySetInnerHTML={{__html:item}}
        //     >  
        //     </li>   
        //     箭頭函數寫法:不用綁定this,因爲箭頭函數不會創建自己的this,它只會從自己的作用域鏈的上一層繼承this。 
        //     <li key={item+index} onClick={()=>this.deleteItem(index)}>    
        //         {item}
        //     </li>                              
        //      拆分成子組件組件寫法
                <XiaojiejieItem 
                    content={item} 
                    index={index}
                    key={item+index}
                />
        )   
    })
} 
</ul>

步驟二:在XiaojiejieItem.js子組件中爲li添加點擊事件handleClick,handleClick中測試是否取得數組index代碼如下:

import React, { Component } from 'react';
class XiaojiejieItem extends Component {
    render() { 
        return ( 
            <li onClick={this.handleClick}>{this.props.content}</li>
        );
    }
    handleClick(){
        console.log(this.props.index);
    }
}
 
export default XiaojiejieItem;

實現效果如下:

點擊服務項後報錯:

即handleClick方法中的this未重新指向XiaojiejieItem ,所以要用bind重新綁定this指向:

<li onClick={this.handleClick.bind(this)}>{this.props.content}</li>

報錯消失,並實現點擊子組件服務項取得數組下標。 

構造函數裏綁定this:

在構造函數裏綁定this指向。這樣綁定性能會高一些,特別是在高級組件開發中,會有很大的作用。XiaojiejieItem 的構造函數代碼如下:

constructor(props){
    super(props)
    this.handleClick=this.handleClick.bind(this)
}

步驟三:通過操作子組件刪除父組件裏的數據。

實現:React有明確規定,子組件時不能操作父組件裏的數據的,所以子組件需要借用一個父組件的方法來修改父組件的內容。

即子組件XiaojiejieItem借用父組件Xiaojiejie的deleteItem方法,現在要作的就是子組件調用這個方法。

父組件向子組件傳方法:

如果子組件要調用父組件方法,其實和傳遞數據差不多,父組件向子組件傳方法,也是在引用子組件時上加屬性

在Xiaojiejie.js的子組件XiaojiejieItem中加入了deleteItem屬性:

<XiaojiejieItem 
content={item} 
index={index}
key={item+index}
deleteItem={this.deleteItem.bind(this)}
/>

記得這裏也要進行this的綁定,如果不綁定子組件是沒辦法找到這個父組件的方法的。

XiaojiejieItem 的handleClick方法代碼如下:

handleClick(){
    // console.log(this.props.index)
    this.props.deleteItem(this.props.index)
}

實現效果:

到此爲止,就算是實現了子組件向父組件傳值。

Xiaojiejie.js完整代碼如下:

import React,{Component,Fragment} from "react"
import './style.css'
import XiaojiejieItem from './XiaojiejieItem'

class Xiaojiejie extends Component{
    constructor(props){
        super(props)//調用父類方法(固定寫法)
        this.state = {
            inputValue:'',//input裏的值,注意符號是:不是=,因爲inputValue、list都是this.state的屬性,屬性間用逗號隔開
            list:['服務1','服務2'] //服務列表,記得加單引號,不然報錯了
        }
    }
    render(){
        return(
            <Fragment> {/*注意Fragment標籤首字母大寫*/}
                <div>
                    <label htmlFor='addList'>增加服務</label>
                    <input id='addList' className='input' value={this.state.inputValue} onChange={this.inputChange.bind(this)} /> {/* input的onChange屬性:狀態值變化;bind重新綁定this指向Xiaojiejie*/} 
                    <button onClick={this.addList.bind(this)}>增加服務</button>
                </div>
                <ul>
                {/* 原本寫死的列表數 */}
                {/* <li>服務1</li>
                    <li>服務2</li> */}
                {/* Easy寫法 */}
                {/* {
                    this.state.list.map(
                        (item)=>{return <li key={item}>{item}</li>} 
                    )
                    }                      
                */}
                {/* 技術胖寫法 */}
                { 
                    this.state.list.map((item,index)=>{
                        return(                           
                        // return加了括號,下面的jsx就可以換行寫,看的更清楚
                        //     普通寫法                 
                        //     <li 
                        //     key={item+index} 
                        //     onClick={this.deleteItem.bind(this,index)} 
                        //     dangerouslySetInnerHTML={{__html:item}}
                        //     >  
                        //     </li>   
                        //     箭頭函數寫法:不用綁定this,因爲箭頭函數不會創建自己的this,它只會從自己的作用域鏈的上一層繼承this。 
                        //     <li key={item+index} onClick={()=>this.deleteItem(index)}>    
                        //         {item}
                        //     </li>                              
                        //      拆分成子組件組件寫法
                            <XiaojiejieItem 
                            content={item} 
                            index={index}
                            key={item+index}
                            deleteItem={this.deleteItem.bind(this)}
                            />
                        )   
                    })
                } 
                </ul>
            </Fragment>
        )
    }
    inputChange(e){     //綁定響應事件改變文本框inputValue的值
        // console.log(this);
        // console.log(e);//e是一堆內容
        // console.log(e.target.value);//15_通過e.target.value打印文本框內的值
        // this.state.inputValue=e.target.value;//錯誤寫法:因爲沒用沒用this.setState方法
        // console.log(this);//輸出undefined,所以要bind方法綁定this
        // console.log(this === window);//false
        this.setState({      //React中改變組件內部的狀態數據的值要用this.setState方法,注意setState的的大小寫
            inputValue:e.target.value
        })
    }
    addList(){      //爲按鈕綁定增加服務項事件
        this.setState({
            list:[...this.state.list,this.state.inputValue], //...這個是ES6的新語法的擴展運算符,名爲擴展運算符。作用把list數組進行了分解,形成了新的數組,然後再進行組合。這種寫法更簡單和直觀,所以推薦這種寫法。
            inputValue:''
        })
    }
    deleteItem(index){ //刪除列表服務事件
        // console.log(index)//測試點擊列表項能否取得相應下標
        // this.state.list.splice(index,1)錯誤寫法
        // this.setState({
        //     list:this.state.list
        // }) 錯誤寫法,即便也能和下面代碼實現看似一樣的效果,但是react中禁止直接修改state的值,後期優化會有性能問題
        let list = this.state.list
        list.splice(index,1)
        this.setState({
            list:list
        })
    }
}
export default Xiaojiejie;

XiaojiejieItem.js完整代碼如下:

import React, { Component } from 'react';
class XiaojiejieItem extends Component {
    constructor(props){
        super(props)
        this.handleClick=this.handleClick.bind(this)
    }
    render() { 
        return ( 
            <li onClick={this.handleClick}>{this.props.content}</li>
        )
    }
    handleClick(){
        // console.log(this.props.index)
        this.props.deleteItem(this.props.index)
    }
}
 
export default XiaojiejieItem;

總結:

①父組件向子組件傳值 、子組件向父組件傳值(父組件向子組件傳方法)都是在子組件加上屬性的形式實現的,然後通過this.props.屬性名的方式訪問。

this.props:

React中,使用外部(被傳入傳入的)數據(通過 this.props 訪問),被傳入的數據可在組件中通過 this.props 在 render() 訪問。

註釋:(props:屬性properties)

this.state:

組件還可以維護其內部的有狀態的數據(通過 this.state 訪問)。

註釋:(state :狀態;有狀態的數據)

②調用方法時要綁定this指向,可以直接在調用處bind綁定,但是更好的是在組件構造函數內綁定,這樣有助於性能優化。

13_React的單項數據流、函數式編程

1.單項數據流

爲Xiaojiejie.js的子組件XiaojiejieItem加入list屬性:

<XiaojiejieItem 
content={item} 
index={index}
key={item+index}
deleteItem={this.deleteItem.bind(this)}
// 單向數據流
list={this.state.list}
/>

XiaojiejieItem.js的handleClick方法加入this.props.list=[]:

handleClick(){
    // console.log(this.props.index)
    this.props.list=[]
    this.props.deleteItem(this.props.index)
}

點擊列表項後報錯:

意思是父組件傳遞給子組件的值是隻讀的,不能在子組件中直接修改父組件的值。如果想在子組件修改父組件的值,只能通過12_父子組件相互傳值中父組件引用子組件時,給子組件傳遞父組件的方法(父組件引用子組件時爲子組件增加屬性),再用父組件的方法才能修改父組件中的值。

2.函數式編程

在面試React時,經常會問道的一個問題是:函數式編程的好處是什麼?

①函數式編程讓我們的代碼更清晰,每個功能都是一個函數。

②函數式編程爲我們的代碼測試代理了極大的方便,更容易實現前端自動化測試。

React框架也是函數式編程,所以說優勢在大型多人開發的項目中會更加明顯,讓配合和交流都得心應手。

14_react developer tools的安裝與使用

1.安裝詳見我另一篇博客:

React開發環境(Chrome瀏覽器)安裝react developer tools的兩個方法,Download the React DevTools for a better experience

https://blog.csdn.net/qq_37279880/article/details/105975542

2.react developer tools插件的三種狀態

插件有三種狀態

灰色:說明頁面不是React寫的

黑色:說明頁面是React寫的,並且處於生成環境當中。

紅色:說明頁面是用React編寫的,並且處於調試環境當中。

3.使用

打開開發者工具,發現Components,這裏你可以清晰的看到React的結構,讓自己寫的代碼更加清晰,你還可以看到組件之間的數據傳遞,再也不用通過console.log來測試程序了,在工作中前端的調試都是在這裏進行的。

 

由於本學習筆記過長,圖片較多,編輯器打字時有些卡頓,所以之後內容寫在另一篇博客:

Easy's React學習筆記(二.進階)

 

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