2018-08-02更新:
不難想象脚作,這時(shí)候聯(lián)系兩個(gè)頁(yè)面的紐帶就是URL值或共同的父組件纲熏。這就引發(fā)了兩個(gè)解決方法:context和React-Redux。
這里表述的不對(duì)
URL傳參是react-router,也有很多種方式實(shí)現(xiàn):參考React Router 頁(yè)面?zhèn)髦档乃姆N方法
redux的底層機(jī)制還是context路操,只是封裝了dispatch方法和state
基于上上周的學(xué)習(xí),搭建了webpack+React的項(xiàng)目環(huán)境千贯,這兩周主要就在寫(xiě)業(yè)務(wù)代碼并基于業(yè)務(wù)需求繼續(xù)學(xué)習(xí)React和穿插的其他一些小知識(shí)屯仗,React入門(mén)主要參考了以下幾個(gè)系列的文章,在理解頁(yè)面DOM渲染搔谴,視圖層等概念的基礎(chǔ)上魁袜,還是非常淺顯易懂的:
玩轉(zhuǎn) React(一)- 前言
React 深入系列1:React 中的元素、組件敦第、實(shí)例和節(jié)點(diǎn)
以下記錄了對(duì)一些概念的理解以及部分思考:
- props和state有什么區(qū)別峰弹,如何使用,父子組件之間傳值芜果;
- 頁(yè)面之間如何傳值鞠呈,怎么拿到類(lèi)似全局變量的一個(gè)值(react-redux相關(guān))
- 如何觸發(fā)render更新組件,如何避免不必要的render調(diào)用(理解生命周期)
- 幾個(gè)細(xì)節(jié)(props覆蓋右钾,條件表達(dá)式簡(jiǎn)化蚁吝,組件初始化旱爆,表單重置,render嵌套HTML標(biāo)簽)
1. props和state有什么區(qū)別窘茁,如何使用怀伦,父子組件之間傳值
我知道React是一個(gè)視圖層的框架,在之前使用的JQuery中山林,我們通過(guò)編寫(xiě)HTML代碼來(lái)設(shè)計(jì)網(wǎng)頁(yè)的結(jié)構(gòu)房待,通過(guò) jquery選擇器$("#")
以及getElementById等 api 來(lái)獲取某個(gè)節(jié)點(diǎn),通過(guò)節(jié)點(diǎn)的 innerHTML捌朴,innerText吴攒,appendChild 等屬性或者方法來(lái)更新視圖,但是在React砂蔽,我只需要關(guān)注數(shù)據(jù)的更新洼怔,React會(huì)幫我完成視圖的更新。
所以左驾,在我看來(lái)镣隶,定義一個(gè)React組件,可以分為兩部分诡右,一部分用來(lái)定義處理數(shù)據(jù)安岂,一部分用來(lái)render返回react元素渲染DOM。
因此帆吻,各組件和頁(yè)面間的傳值成了我首要考慮的問(wèn)題也是遇到問(wèn)題最多的地方域那,而組件根據(jù)props和state計(jì)算得到對(duì)應(yīng)頁(yè)面的UI,這兩個(gè)參數(shù)有什么區(qū)別猜煮,如何定義呢次员?
- props 是組件對(duì)外的接口,state 是組件對(duì)內(nèi)的接口王带,組件通過(guò)state參數(shù)渲染元素淑蔚,上下層組件通過(guò)props參數(shù)傳遞數(shù)據(jù),state是可變的愕撰,props是只讀屬性刹衫;
- 不能直接修改state,采用setState方法搞挣,state屬性改變會(huì)重發(fā)render更新組件做到視圖和數(shù)據(jù)的綁定带迟;
- 調(diào)用setState,組件的state并不會(huì)立即改變囱桨,setState只是把要修改的狀態(tài)放入一個(gè)隊(duì)列中邮旷,React會(huì)優(yōu)化真正的執(zhí)行時(shí)機(jī),并且React會(huì)出于性能原因蝇摸,可能會(huì)將多次setState的狀態(tài)修改合并成一次狀態(tài)修改婶肩。所以不能依賴(lài)當(dāng)前的state,計(jì)算下個(gè)state貌夕。
因此一個(gè)完整的父子組件之間傳值的流程如下(以主組件調(diào)用側(cè)欄組件為例):
- 子組件props屬性定義及類(lèi)型校驗(yàn)
SlideFrame.propTypes = {
width: React.PropTypes.any, //寬度
title: React.PropTypes.string, //標(biāo)題
show: React.PropTypes.bool, //是否顯示
hasMask: React.PropTypes.bool, //是否有遮罩層
onClose: React.PropTypes.func, //點(diǎn)擊遮罩層或右上方x時(shí)觸發(fā)的事件
content: React.PropTypes.oneOfType([React.PropTypes.func, React.PropTypes.string]), //內(nèi)容component律歼,包裹后的元素添加this.props.close方法進(jìn)行側(cè)滑關(guān)閉
afterClose: React.PropTypes.func, //關(guān)閉后觸發(fā)的事件,用于更新外層的show值
params: React.PropTypes.object, //外部傳入內(nèi)部組件props
hasFooter: React.PropTypes.bool //是否有低端操作區(qū)
};
SlideFrame.defaultProps = {
width: '50vw',
onClose: ()=>{},
okText: '保存',
cancelText: '取消',
hasMask: true,
afterClose: ()=>{},
params: {},
hasFooter: true
};
- 父組件傳值
<SlideFrame title={ showSlideFrameContent.isNew ? messages('rep.distribution.details.create')/*新建分配*/ : messages('rep.distribution.details.edit')/*編輯分配*/}
show={showSlideFrame}
content={ReportDistributionMaintain}
onClose={this.closeSlide}
params={showSlideFrameContent}
/>
- 父組件更新state值啡专,重發(fā)render更新傳入子組件的值
ReportDistributionService.getCopyReportDetail(record.reportLineOID).then((response) => {
this.setState({
loading: false,
showSlideFrameContent :{
isNew: false,
reportDetail: response.data,
reportLineOID: record.reportLineOID
}
})
});
ReportDistributionService.getDistributionPeopleList(page, pageSize,record.reportLineOID).then((response) => {
response.data.map((item,index) => {
item.index = index + page * pageSize + 1;
item.key = item.userOID;
item.departmentName = item.department.name;
});
this.setState({
loading: false,
showSlideFrameContent :{
isNew: false,
pagination: {
total: Number(response.headers['x-total-count']) ? Number(response.headers['x-total-count']) : 0,
current: this.state.page + 1
},
distributionPeopleList: response.data,
reportLineOID:record.reportLineOID
}
});
});
- 子組件接收props
componentWillReceiveProps(nextProps) {
//console.log("子組件接收props");
this.setState({loading: true});
// props每變一次就會(huì)調(diào)用一次险毁,所以賦值如果寫(xiě)一起會(huì)被空值覆蓋
if (nextProps.params.reportDetail) {
console.log(nextProps.params.reportDetail);
this.setState({
isNew: nextProps.params.isNew,
reportDetail:nextProps.params.reportDetail,
reportLineOID: nextProps.params.reportLineOID
}, () => {
if(nextProps.params.reportDetail.dataArea === "5"){
this.getCopyReportCorporation(); //獲取 單條 報(bào)表副本 已選法人列表
}
if(nextProps.params.reportDetail.dataArea === "8"){
this.getCheckedSetOfBookList(); //獲取 單條 報(bào)表副本 已選賬套列表
}
if(nextProps.params.reportDetail.dataArea === "4" || nextProps.params.reportDetail.dataArea === "6"){
this.getCheckedDepList(); //獲取 單條 報(bào)表副本 已選部門(mén)列表
}
});
this.setState({loading: false});
}
if (nextProps.params.newCopyReportDetail) {
this.setState({
loading: false,
isNew: nextProps.params.isNew,
newCopyReportDetail:nextProps.params.newCopyReportDetail
});
}
if (nextProps.params.distributionPeopleList) {
this.setState({
loading: false,
isNew: nextProps.params.isNew,
pagination: nextProps.params.pagination,
reportLineOID: nextProps.params.reportLineOID,
data: nextProps.params.distributionPeopleList
});
}
}
這里有一個(gè)小細(xì)節(jié),因?yàn)楦聅tate的時(shí)候數(shù)據(jù)必須分兩次拿到们童,導(dǎo)致傳入的props數(shù)據(jù)每次都有一個(gè)為空畔况,如果取值時(shí)也一次性賦值,則會(huì)覆蓋
參考文章:React中state和props分別是什么慧库?
2.頁(yè)面之間如何傳值院领,怎么拿到類(lèi)似全局變量的一個(gè)值(react-redux相關(guān))
這周的業(yè)務(wù)中有一個(gè)需求抽米,是拿到當(dāng)前的語(yǔ)言環(huán)境,是中文還是英文,顯然闰歪,這應(yīng)該是一個(gè)類(lèi)似全局變量的值;另一個(gè)需求悯舟,是講當(dāng)前頁(yè)active的tab值通過(guò)路由跳轉(zhuǎn)后傳到詳情頁(yè)患整。
上面是父子組件之間的傳值,尚有一個(gè)公用的接口可以傳遞數(shù)據(jù)济舆,那兩個(gè)沒(méi)有聯(lián)系的頁(yè)面或者說(shuō)兄弟組件之間如何傳值呢卿泽,要傳值,就要找到聯(lián)系他們的紐帶滋觉,不難想象签夭,這時(shí)候聯(lián)系兩個(gè)頁(yè)面的紐帶就是URL值或共同的父組件。這就引發(fā)了兩個(gè)解決方法:context和React-Redux椎瘟。
關(guān)于context覆致,這篇文章說(shuō)的很清楚React 組件通信之 React context
React是基于單向數(shù)據(jù)傳遞的,一般對(duì)于兄弟組件之間的通信肺蔚,是通過(guò)它們共同的祖先組件進(jìn)行的煌妈,即狀態(tài)提升Lifting State Up,狀態(tài)提升的意思是宣羊,當(dāng)組件 A 需要依賴(lài)另外一個(gè)組件 B 的內(nèi)部狀態(tài)璧诵,而他們又不是父子關(guān)系時(shí),需要將組件 B 的內(nèi)部狀態(tài)提升到他們公共的祖先組件中管理仇冯。這樣他們就都可以通過(guò)屬性接收到這份數(shù)據(jù)了之宿。
當(dāng)組件 B 需要對(duì)數(shù)據(jù)進(jìn)行變更時(shí),可以通過(guò)函數(shù)屬性來(lái)通知祖先組件對(duì)數(shù)據(jù)更新苛坚,然后重新傳遞給子組件比被。
但但組件嵌套比較復(fù)雜的時(shí)候色难,這個(gè)方法著實(shí)很麻煩,context則能做到讓組件樹(shù)全局共享某狀態(tài)等缀。
context的使用方法也很簡(jiǎn)單枷莉,在頂部父組件聲明context,那它的所有子組件可以通過(guò) this.context 直接獲取得到尺迂,項(xiàng)目業(yè)務(wù)中采用這個(gè)方式存取路由:
//頂部父組件
Main.childContextTypes = {
router: React.PropTypes.object
};
//子組件
ReportDistribution.contextTypes = {
router: React.PropTypes.object
};
關(guān)于React-Redux
首先笤妙,Redux 和 React-redux 并不是同一個(gè)東西。Redux 是一種架構(gòu)模式(Flux 架構(gòu)的一種變種)噪裕,它不關(guān)注你到底用什么庫(kù)蹲盘,你可以把它應(yīng)用到 React 和 Vue,甚至跟 jQuery 結(jié)合都沒(méi)有問(wèn)題膳音。而 React-redux 就是把 Redux 這種架構(gòu)模式和 React.js 結(jié)合起來(lái)的一個(gè)庫(kù)召衔,就是 Redux 架構(gòu)在 React.js 中的體現(xiàn)。
參考文章:跟著例子一步步學(xué)習(xí)redux+react-redux
(這里沒(méi)看完严蓖,待更新薄嫡。。颗胡。)
另外還有一種解決方式:ref屬性 毫深,這個(gè)和context一樣是react官方不推薦使用的屬性,因?yàn)榭赡軙?huì)在未來(lái)的版本更新中被取消毒姨。
參考文章:React的Refs方法獲取DOM實(shí)例 和 訪問(wèn)子組件方法及屬性
3.如何觸發(fā)render更新組件哑蔫,如何避免不必要的render調(diào)用(理解生命周期
上面了解到,當(dāng)state狀態(tài)更新時(shí)弧呐,react會(huì)重發(fā)render闸迷,因此,在業(yè)務(wù)邏輯中常常采用子組件中componentWillReceiveProps接收props俘枫,更新state腥沽,觸發(fā)重新渲染的方式更新視圖,但是調(diào)試的輸出的時(shí)候經(jīng)常發(fā)現(xiàn)觸發(fā)了不止一次鸠蚪,又或者當(dāng)新建和不同表格行的編輯都共用一個(gè)組件時(shí)今阳,沒(méi)有正確的重發(fā)render導(dǎo)致表單無(wú)法重置。所以render和state的關(guān)系到底是什么茅信?盾舌?
這篇文章或許會(huì)有所幫助,【react】利用shouldComponentUpdate鉤子函數(shù)優(yōu)化react性能以及引入immutable庫(kù)的必要性蘸鲸,由于業(yè)務(wù)有點(diǎn)復(fù)雜這里并沒(méi)有來(lái)得及采用這個(gè)函數(shù)做優(yōu)化了妖谴,但是也值得一看。
4.幾個(gè)小tips
- 表達(dá)式優(yōu)化
!!相當(dāng)于Boolean()
eg:this.props.params.i ? true : false
寫(xiě)成!!this.props.params.id
比較好 - render函數(shù)代碼中嵌套多個(gè)HTML 標(biāo)簽酌摇,需要使用一個(gè)標(biāo)簽元素包裹他
(axios
react-redux
react-router
待更新膝舅。嗡载。。)