????????在react中是單向數(shù)據(jù)流的設(shè)計(jì),?即?只有父組件可以傳遞數(shù)據(jù)給子組件,而沒有子組件傳遞數(shù)據(jù)給父組件的概念.?以正確的技術(shù)說明,是 擁有者組件 可以設(shè)置 被擁有者組件 中的資料,也就是主人與仆人的關(guān)系。
那么子組件要傳遞數(shù)據(jù)給父組件該如何溝通呢?
換句話說就是,?react 如何將子組件的值暴露讓父組件獲取到?
????????可以采用一種迂回的方法,?在父組件中設(shè)置一個(gè)方法(函數(shù)),?將其通過props傳遞給子組件,?然后在子組件中更新state的狀態(tài),并調(diào)用父組件中傳過來的方法,?將state數(shù)據(jù)作為參數(shù)傳遞給父組件.?這樣,?改變父組件的狀態(tài),從而改變受父組件控制的所有子組件的狀態(tài).?這就是狀態(tài)提升的概念.? ?用官方的原話就是:? ? '共享 state(狀態(tài)) 是通過將其移動(dòng)到需要它的組件的最接近的共同祖先組件來實(shí)現(xiàn)的。 這被稱為“狀態(tài)提升(Lifting State Up)'。
? ? 官方參考網(wǎng)址:?http://www.css88.com/react/docs/lifting-state-up.html
? ? 下面舉個(gè)例子說明:
? ? ? ? App.js文件? ?
import? React, { Component } from 'react'
import Item from './Item'
export default class App extends Component {
????constructor(props) {
????????super(props)
????????this.state = {
????????????options: [
????????????????{name:'(1)免費(fèi)行李', value: 1 },
????????????????{name:'2', value: 2 },
????????????????{name:'3', value: 3 } ],
????????????price: 0
????????}
? ?}
????changePrice = (value) => {
????????let price = 800
????????if(value === 1)? price = 0
????????else
????????price *= value - 1
????????this.setState({price: price}) }
????render() {
????return (
????????{this.state.price}
)? }}
Item.js文件
import React, { Component } from 'react'
export default class Item extends Component {
????constructor(props) {
????????super(props)
????????this.handleChange = this.handleChange.bind(this)
????}
????handleChange(e){
????this.props.changePrice(e.target.value)
????}
????render() {
????????var options = []
????????var optionArray = this.props.optionArray
????????options = optionArray.map(function(item, index){
????????????return ( {item.name} )
????????})
????????return ( {options} )
????}
}
說明:
?1. 先綁定(bind)住render有用到的方法
在父組件與子組件各有用到一個(gè)自己的方法changePrice,并在render中作賦值,在React中需要bind過才會(huì)把this對(duì)住,因?yàn)樵趓ender的return語句中使用,它在重渲染(re-render)時(shí)會(huì)再次建立新的方法(函數(shù))內(nèi)容值,這樣會(huì)有效能上的影響,所以要先作綁定的事,然后再render的return里面用。關(guān)于bind(this)的一些理解,在我的另一篇文章,可以參考:http://www.itdecent.cn/p/f7f2636d16a9
先綁定要在類的contructor里作,像下面這樣,我這寫一個(gè)父組件而已,子組件一樣:
constructor(props) {super(props)this.state = {price:0}//先bind類中方法this.changePrice =this.changePrice.bind(this)? }
之后在render的return要改成這樣:
render() {return(
{this.state.price}
);? }
2. 校正state(狀態(tài))里的資料,以及提升到父組件去
在子組件中的state(狀態(tài))中的資料是不是有那么必要放在子組件中,如果你還有第二個(gè)子組件、第三、第四…,它們都要用例如這里的選中資料,你放在這個(gè)子組件是違反了上面說的應(yīng)用領(lǐng)域全局資料思維的。
先看一下子組件目前的state,是長(zhǎng)這個(gè)樣子:
this.state = {names: ['(1)免費(fèi)行李','2','3'],values: ['1','2','3'],selectName:'',prices:'0'}
這里要先校正一下,names與values是代表選項(xiàng)中的名與值,它們是有關(guān)聯(lián)的,所以應(yīng)該是這樣的下面結(jié)構(gòu)才是好些的,value如果是要用來代表數(shù)字,就用數(shù)字就行不需要用字串:
options: [? {name:'(1)免費(fèi)行李',value:1},? {name:'2',value:2},? {name:'3',value:3}]
選中了哪個(gè)選項(xiàng)這個(gè)狀態(tài)資料,還是要先放在子組件中,因?yàn)樽咏M件中有選項(xiàng)盒,與觸發(fā)的更動(dòng)方法,但選項(xiàng)的資料可以移到上層的父組件中:
這是上層App.js中的狀態(tài):
this.state = {options: [? ? ? ? {name:'(1)免費(fèi)行李',value:1},? ? ? ? {name:'2',value:2},? ? ? ? {name:'3',value:3}? ],price:0}
父組件也改用把state里面的選項(xiàng)值,用props值給子組件,所以在render里語句改成下面這樣:
render() {return(
{this.state.price}
)? }
子組件中這時(shí)可以用this.props.optionArray接到傳入的選項(xiàng)值,所以在render方法中,改用這個(gè)來代替之前的this.state.names與this.state.values,簡(jiǎn)單改寫如下:
varoptions = []varprices =this.state.pricesvaroptionArray =this.props.optionArrayfor(vari =0; i< optionArray.length; i++) {? ? ? options.push({optionArray[i].name})? }
注: 這里不用for...in語句而用for語句,是因?yàn)閒or...in語句是個(gè)不建議用在數(shù)組資料的語法,它并不會(huì)保證取到數(shù)組成員里的順序。for...in只會(huì)用在對(duì)象的尋遍中。
更精簡(jiǎn)的寫法是用Array.map,如下:
varoptions = []varprices =this.state.pricesvaroptionArray =this.props.optionArrayoptions = optionArray.map(function(item, index){return({item.name})})
接著,如果依選項(xiàng)選中然后計(jì)算價(jià)格這件事,規(guī)劃中應(yīng)該是整個(gè)應(yīng)用來作的,例如有可能還有其他的組件中也有其他的選項(xiàng),最后統(tǒng)一要來算價(jià)格,所以計(jì)算價(jià)格這件事,也應(yīng)該放到父組件去,所以如同上面的改寫一樣,把子組件的prices狀態(tài)與相關(guān)計(jì)算的代碼,都提到父組件,這個(gè)子組件純用來當(dāng)選項(xiàng)盒用而已。子組件此時(shí)連state都可以不用有。
因?yàn)檎麄€(gè)改寫過的代碼會(huì)多些,所以我把父組件與子組件中的代碼整個(gè)貼上。
父組件App.js:
importReact, { Component }from'react';importItemfrom'./Item'exportdefaultclassAppextendsComponent{constructor(props) {super(props)this.state = {options: [? ? ? ? {name:'(1)免費(fèi)行李',value:1},? ? ? ? {name:'2',value:2},? ? ? ? {name:'3',value:3}? ? ? ],price:0}this.changePrice =this.changePrice.bind(this)? }? changePrice(value){varprice =800;if(value ===1) price =0elseprice = (value -1) * pricethis.setState({price: price})? }? render() {return(
{this.state.price}
)? }}
子組件Item.js:
importReact, { Component }from'react'exportdefaultclassItemextendsComponent{constructor(props) {super(props)this.handleChange =this.handleChange.bind(this)? }? handleChange(e){this.props.changePrice(e.target.value)? }? render() {varoptions = []varoptionArray =this.props.optionArray? ? options = optionArray.map(function(item, index){return({item.name})? ? })return(
{options}
)? }}
3. 目前最終進(jìn)化版本
這個(gè)版本有幾個(gè)改進(jìn)如下,供參考:
用let/const取代var。
不用分號(hào)(;)作為語句結(jié)尾。
Item子組件改用函數(shù)定義方式,取代原先的組件定義方式。
能合并的語句都合并。
函數(shù)全用箭頭函數(shù)(注意需額外加裝babel-plugin-transform-class-properties)。
App.js
importReact, { Component }from'react'importItemfrom'./Item2'exportdefaultclassAppextendsComponent{constructor(props) {super(props)this.state = {options: [? ? ? ? {name:'(1)免費(fèi)行李',value:1},? ? ? ? {name:'2',value:2},? ? ? ? {name:'3',value:3}? ? ? ],price:0}? }? changePrice =(value) =>{letprice =800if(value ===1) price =0elseprice *= value -1this.setState({price: price})? }? render() {return(
{this.state.price}
)? }}
Item.js
importReactfrom'react'constItem =(props) =>{constoptionArray = props.optionArrayconstoptions = optionArray.map((item, index) =>{return({item.name})? })return(
{props.changePrice(e.target.value)}}>? ? ? ? ? ? {options}
)}exportdefaultItem
參考文檔: 1.?https://www.cnblogs.com/zhangbob/p/6962138.html?utm_source=itdadao&utm_medium=referral
2.?http://www.css88.com/react/docs/lifting-state-up.html官方文檔