state叫狀態,是每個類式組件都有的屬性,但函數式組件,沒有state。html
state是一個對象,什麼值均可以定義。react
在任何類式組件的構造函數中,能夠用this.state = {} 來給類的實例添加state屬性,表示「狀態」。npm
在render()函數的return中,能夠用{this.state.a}插值來顯示出每個屬性的值編程
import React from "react"; export default class App extends React.Component { constructor() { super(); //組件的內部狀態,state屬性 this.state = { a : 100 } } render() { return <div> <h1>{this.state.a}</h1> </div> } }
點擊按鈕以後,讓100加1數組
import React from "react"; import ReactDOM from "react-dom"; export default class App extends React.Component { constructor() { super(); this.state = { a : 100 } } render() { return <div> <button onClick={()=>{ this.setState({ a : this.state.a + 1 }) }}>按我</button> <h1>{this.state.a}</h1> </div> } }
注意:dom
React中事件監聽,要寫在標籤上。ide
事件名onClick而不是onclick,注意大寫字母。由於React將事件名都進行了拓展,因此onClick是React本身的方法。同理,全部事件名on後面的首字母都是大寫:onMouseEnter、onDoubleClick、onKeyDown。函數
onClick=後面緊跟{},表示插值。大括號中是一個箭頭函數,這個函數必須是箭頭函數,不然this錯誤。學習
<button onClick={()=>{ }}></button>
setState是定義在React.Component類中的方法,因此任何一個組件都可以無腦調用this.setState()。表示「設置state」。ui
this.setState({要設置的k : 新的v});
setState不只可以改變實例的state的屬性值,並且能產生視圖刷新。而若是不用setState(),只是讓state的屬性值進行改變,視圖是不刷新的。
錯誤的:
export default class App extends React.Component { constructor() { super(); this.state = { a : 100 } } render() { return <div> <button onClick={()=>{ this.state.a++; }}>按我</button> <h1>{this.state.a}</h1> </div> } }
以前的onClick後面直接跟上了{()=>{}},實際上能夠提出來,封裝成函數。
import React from "react"; export default class App extends React.Component{ constructor(){ super(); this.state = { a : 100 } } add(){ this.setState({ a:this.state.a + 1 }); } render(){ return <div> <h1>{this.state.a}</h1> <button onClick={()=>{this.add()}}>按我加1</button> <button onClick={this.add.bind(this)}>按我加1</button> </div> } };
方法的執行能夠不經過箭頭函數,可是很差,由於這樣寫不能傳遞參數
注意,提煉成爲組件的方法(實際上寫在了構造器的prototype上,實例的原型上),在onClick調用的時候,必須寫bind(this),將調用的這個函數的上下文綁定爲組件的實例。能夠當作是一個固定的語法!
如何傳參數?
import React from "react"; import ReactDOM from "react-dom"; export default class App extends React.Component { constructor() { super(); this.state = { a : 100 } } //單擊事件的處理函數 add(n){ this.setState({ a : this.state.a + n }); } render() { return <div> <button onClick={()=>{this.add(1)}}>按我</button> <button onClick={()=>{this.add(2)}}>按我</button> <button onClick={()=>{this.add(3)}}>按我</button> <h1>{this.state.a}</h1> <div style={{ "width" : this.state.a + "px", "height": this.state.a + "px", "backgroundColor" : "orange", "transform" : "rotate(" + this.state.a + "deg)" }}></div> </div> } }
React、Vue以及已通過時Angular都是MVVM模式,都有一個特色,就是:
數據驅動視圖:數據變化了,視圖自動變化
MVVM模式經典的4句話:
1)數據變化,視圖就會自動變化
2)視圖變化的緣由,必定是數據變化了
3)數據是視圖的本質
4)視圖是數據的表現
咱們之後不再用關心DOM結構了,只關心數據,數據變化,視圖自動變化
如今只須要用setState()來改變組件的實例的state,視圖就會自動變化,後面你將知道,視圖變化的緣由是由於組件進入了新的生命週期,也將知道視圖更新的效率由於有的Virtual DOM從而變的很快。
知識點:html標籤能夠加ref(reference引用)屬性,在組件內部能夠經過this.refs來引用這個DOM元素。
<input type="text" ref="nameTxt"/>
獲取標籤的值:
var val = this.refs.nameTxt.value
import React from "react"; export default class App extends React.Component { constructor() { super(); this.state = { arr: [ { "id": 1, "name": "小明", "age": 12, "sex": "男" }, { "id": 2, "name": "小紅", "age": 13, "sex": "女" }, { "id": 3, "name": "小剛", "age": 14, "sex": "男" }, { "id": 4, "name": "小白", "age": 15, "sex": "男" } ] } } //添加學員 addList(){ //獲取值 var name = this.refs.nameTxt.value; var age = this.refs.ageTxt.value; var sex = this.refs.sexTxt.value; //改變state必須用setState()方法 this.setState({ arr:[ // 原來的項是不變 ...this.state.arr, // 只增長一項 { //id : 6 id:this.state.arr.reduce((a,b)=>{ return a.id > b.id ? a : b }).id + 1, name, age, sex } ] }) } //刪除列表 delList(id){ this.setState({ arr:this.state.arr.filter(item=>item.id !=id) }); } render(){ return <div> <p>姓名:<input type="text" ref="nameTxt" /></p> <p>年齡:<input type="text" ref="ageTxt" /></p> <p>性別:<input type="text" ref="sexTxt" /></p> <button onClick={()=>{this.addList()}}>添加</button> <ul> { this.state.arr.map(item=>{ return <li key={item.id}> {item.id} -- {item.name}--年齡{item.age}歲,性別{item.sex} <button onClick={()=>{this.delList(item.id)}}>刪除</button> </li> }) } </ul> </div> } }
React中都是純函數編程。
import React from "react"; export default class App extends React.Component{ constructor(){ super(); this.state = { r : 20, g : 200, b : 123 } } //變化顏色 setColor(k,v){ this.setState({ [k] : v }) } render(){ return <div> <div style={{ "width":"200px", "height":"200px", "background":`rgb(${this.state.r},${this.state.g},${this.state.b})`}}> </div> <p> <input type="range" max={255} value = {this.state.r} onChange={(e)=>{this.setColor("r",e.target.value)}} /> <span>{this.state.r}</span> </p> <p> <input type="range" max={255} value={this.state.g} onChange={(e)=>{this.setColor("g",e.target.value)}} /> <span>{this.state.g}</span> </p> <p> <input type="range" max={255} value={this.state.b} onChange={(e)=>{this.setColor("b",e.target.value)}} /> <span>{this.state.b}</span> </p> </div> } };
若是一個表單元素,和state的一個值進行了「關聯」:
1)state的值就是表單元素的值;
2)改變表單元素,就會改變state的值。
咱們叫作這個表單元素和數據進行了「雙向數據綁定」,也叫做表單元素「受控」。
在React中,實現雙向數據綁定(實現表單元素受控)的套路:
加上value屬性實現從state中「要」值;
加上onChange事件實現「設置」state的值。
若是一個組件內部,全部表單元素都有state的數據,進行了雙向數據綁定,此時稱爲「受控組件」。
<p> <input type="range" min={0} max={255} value={this.state.b} onChange={(e)=>{this.setColor("b" , e.target.value)}} /> </p>
簡單的說「一個表單元素受控」,等價於「這個表單元素有一個值和他雙向綁定」。
全部的表單元素受控,咱們就說組件受控。
結構:輸入框、發佈按鈕、內容清空按鈕,一串文字「當前88/140字」
當內容超過140字,則發佈按鈕不能點,文字變紅
實時顯示字數,當框中有內容,按鈕能夠點擊清空
若是讓一個元素是否使用某一個類,React官方建議安裝classnames依賴
npm install --save classnames
import React from "react"; import classnames from "classnames"; export default class App extends React.Component{ constructor(){ super(); this.state = { txt: "" } } render(){ const length = this.state.txt.length; return <div> <textarea cols="30" rows="10" value={this.state.txt} onChange={(e)=>{this.setState({txt:e.target.value})}} > </textarea> <p> <button disabled={length == 0 || length>140}>發佈</button> <button disabled={length==0} onClick={()=>{this.setState({txt:""})}}> 清空 </button> <span className={classnames({"danger":length > 140})}> 已寫{length}/140字 </span> </p> </div> } };
什麼是受控?
一個表單元素的value值和state中某個屬性息息相關:
這個表單元素的值,來自於state中的屬性
更改表單元素的值,可以更改state中的值
也叫雙向數據綁定,不過React中稱爲「受控組件(Controller Component)」
Vue才叫雙向數據綁定。
在React中,全部表單元素的受控方式同樣,都是value={},onChange={}
import React from "react"; var classNames = require('classnames'); export default class App extends React.Component { //構造函數 constructor() { super(); this.state = { a : "我是默認的a值", b : 50, } } render() { return ( <div> <p> <input type="text" value={this.state.a} onChange={(e) => { this.setState({ a: e.target.value }) }} /> <span>{this.state.a}</span> </p> <p> <input type="range" value={this.state.b} onChange={(e) => { this.setState({ b: e.target.value })}} /> <span>{this.state.b}</span> </p> </div> ) } };
this.state = { c : "廣州" }
<p> <select value={this.state.c} onChange={(e) => { this.setState({ c: e.target.value }) }} > <option value="廣州">廣州</option> <option value="深圳">深圳</option> <option value="佛山">佛山</option> <option value="東莞">東莞</option> <option value="雲浮">雲浮</option> </select> <span>{this.state.c}</span> </p>
單選按鈕受控的套路:checked={}、value="" 、onChange={}
<p> <input type="radio" name="sex" value="男" checked={this.state.e == '男'} onChange={(e) => { this.setState({ d: e.target.value }) }} />男 <input type="radio" name="sex" value="女" checked={this.state.e == '女'} onChange={(e) => { this.setState({ d: e.target.value })}} />女 <span>【{this.state.d}】</span> </p>
複選框和上面全部表單元素都不同:
要靠checked={}獲得值,要判斷includes
要寫一個函數,傳入將要驗證的值,根據這個值是否是已經在數組中,再決定刪除、添加。
setF(word){ if(this.state.f.includes(word)){ //若是這個值已經在f數組中,則刪除 this.setState({ f : this.state.f.filter(item=>item != word) }); }else{ //若是這個值不在f數組中,則加入數組 this.setState({ f : [...this.state.f, word] }); } }
<p> 愛好: <input type="checkbox" value="看書" checked={this.state.f.includes('看書')} onChange={(e) => { this.setF("看書") }} />看書 <input type="checkbox" value="游泳" checked={this.state.f.includes('游泳')} onChange={(e) => { this.setF("游泳") }} />游泳 <input type="checkbox" value="打球" checked={this.state.f.includes('打球')} onChange={(e) => { this.setF("打球") }} />打球 <span>【{this.state.f.join(',')}】</span> </p>
這個案例學習DOM的上下樹
控制元素是否顯示或隱藏,不要用display屬性
而是用三元運算符控制DOM元素是否上下樹。若是上數寫標籤,若是不上樹寫null
不論是作什麼案例,都是兩大部分:①、寫JSX侵入DOM標籤,②、事件監聽,改變state
import React from "react"; export default class App extends React.Component{ // 構造函數 constructor(){ super(); this.state = { arr:[ { "id": 1, "name": "小明", "age": 12, "sex": "男" }, { "id": 2, "name": "小紅", "age": 13, "sex": "女" }, { "id": 3, "name": "小剛", "age": 14, "sex": "男" }, { "id": 4, "name": "小白", "age": 15, "sex": "男" } ], showCols:['姓名',"年齡","性別"] } } setChangeCols(word){ if(this.state.showCols.includes(word)){ this.setState({ showCols:this.state.showCols.filter(item=>item !=word) }) }else{ this.setState({ showCols:[ ...this.state.showCols, word ] }); }; } render(){ return <div> <div> <input type="checkbox" value="姓名" checked={this.state.showCols.includes("姓名")} onChange={(e)=>{this.setChangeCols("姓名")}} />姓名 <input type="checkbox" value="年齡" checked={this.state.showCols.includes("年齡")} onChange={(e)=>{this.setChangeCols("年齡")}} />年齡 <input type="checkbox" value="性別" checked={this.state.showCols.includes("性別")} onChange={(e)=>{this.setChangeCols("性別")}} />性別 </div> <span>{this.state.showCols}</span> <table> <tbody> <tr> <th>ID</th> {this.state.showCols.includes("姓名") ? <th>姓名</th> : null} {this.state.showCols.includes("年齡") ? <th>年齡</th> : null} {this.state.showCols.includes("性別") ? <th>性別</th> : null} </tr> { this.state.arr.map(item=>{ return <tr key={item.id}> <td>{item.id}</td> {this.state.showCols.includes("姓名") ? <td>{item.name}</td>:null} {this.state.showCols.includes("年齡") ? <td>{item.age}</td>:null} {this.state.showCols.includes("性別") ? <td>{item.sex}</td>:null} </tr> }) } </tbody> </table> </div> } };
額外提供的數據:
[ { "name" : "廣東省", "city" : [ { "name":"廣州", "area":[ "天河區", "白雲區", ... ] }, { "name":"深圳", "area":[ "福田區", "南山區" ] } .... ] }, .... ]
forEach、filter、map、reduce函數都是表達式,而不是語句體。if和for語句是語句體。
import React from "react"; import city from "./city.js"; export default class App extends React.Component{ constructor(){ super(); this.state = { "province":"廣東省", "city":"廣州市", "area":"天河區" } } render(){ //循環遍歷省份 const showProvinces = ()=>{ //遍歷city數據,提取每一項省份成爲option var arr=[]; city.forEach((item,index)=>{ arr.push(<option key={index} value={item.name}>{item.name}</option>) }); return arr; } //循環遍歷市,顯示哪個城市,須要根據state的province屬性的省份選擇城市 const showCitys = ()=>{ var arr = []; // 先篩選省份,再次篩選對應城市列表 var cityArr = city.filter(item=>item.name == this.state.province)[0].city; cityArr.forEach((item,index)=>{ arr.push(<option key={index} value={item.name}>{item.name}</option>) }); return arr; }; //顯示區縣 const showAreas = ()=>{ var arr = []; // 先篩選省份,再次篩選對應城市列表 var cityArr = city.filter(item=>item.name == this.state.province)[0].city; //根據市,得出區,只需選擇區的第一項 var areaArr = cityArr.filter(item => item.name == this.state.city)[0].area //最後根據篩選出的市,遍歷區 areaArr.forEach((item,index)=>{ arr.push(<option key={index} value={item}>{item}</option>) }) return arr; } return <div> 省份:<select value={this.state.province} onChange={(e)=>{ this.setState({ "province":e.target.value, "city":city.filter(item=>item.name == e.target.value)[0].city[0].name }) }} > {showProvinces()} </select> 城市:<select value={this.state.city} onChange={(e)=>{ this.setState({"city":e.target.value}) }} > {showCitys()} </select> 區縣:<select value={this.state.area} onChange={(e)=>{ this.setState({"area":e.target.value}) }} > {showAreas()} </select> </div> } };