React組件傳值與路由傳參

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ì)一些概念的理解以及部分思考:

  1. props和state有什么區(qū)別峰弹,如何使用,父子組件之間傳值芜果;
  2. 頁(yè)面之間如何傳值鞠呈,怎么拿到類(lèi)似全局變量的一個(gè)值(react-redux相關(guān))
  3. 如何觸發(fā)render更新組件,如何避免不必要的render調(diào)用(理解生命周期)
  4. 幾個(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ū)別猜煮,如何定義呢次员?

  1. props 是組件對(duì)外的接口,state 是組件對(duì)內(nèi)的接口王带,組件通過(guò)state參數(shù)渲染元素淑蔚,上下層組件通過(guò)props參數(shù)傳遞數(shù)據(jù),state是可變的愕撰,props是只讀屬性刹衫;
  2. 不能直接修改state,采用setState方法搞挣,state屬性改變會(huì)重發(fā)render更新組件做到視圖和數(shù)據(jù)的綁定带迟;
  3. 調(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

  1. 表達(dá)式優(yōu)化
    !!相當(dāng)于Boolean()
    eg: this.props.params.i ? true : false寫(xiě)成 !!this.props.params.id比較好
  2. render函數(shù)代碼中嵌套多個(gè)HTML 標(biāo)簽酌摇,需要使用一個(gè)標(biāo)簽元素包裹他

(axios
react-redux
react-router
待更新膝舅。嗡载。。)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末仍稀,一起剝皮案震驚了整個(gè)濱河市鼻疮,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌琳轿,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,640評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件耿芹,死亡現(xiàn)場(chǎng)離奇詭異崭篡,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)吧秕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,254評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)琉闪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人砸彬,你說(shuō)我怎么就攤上這事颠毙。” “怎么了砂碉?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,011評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵蛀蜜,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我增蹭,道長(zhǎng)滴某,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,755評(píng)論 1 294
  • 正文 為了忘掉前任滋迈,我火速辦了婚禮霎奢,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘饼灿。我一直安慰自己幕侠,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,774評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布碍彭。 她就那樣靜靜地躺著晤硕,像睡著了一般。 火紅的嫁衣襯著肌膚如雪硕旗。 梳的紋絲不亂的頭發(fā)上窗骑,一...
    開(kāi)封第一講書(shū)人閱讀 51,610評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音漆枚,去河邊找鬼创译。 笑死,一個(gè)胖子當(dāng)著我的面吹牛墙基,可吹牛的內(nèi)容都是我干的软族。 我是一名探鬼主播刷喜,決...
    沈念sama閱讀 40,352評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼立砸!你這毒婦竟也來(lái)了掖疮?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,257評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤颗祝,失蹤者是張志新(化名)和其女友劉穎浊闪,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體螺戳,經(jīng)...
    沈念sama閱讀 45,717評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡搁宾,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,894評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了倔幼。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片盖腿。...
    茶點(diǎn)故事閱讀 40,021評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖损同,靈堂內(nèi)的尸體忽然破棺而出翩腐,到底是詐尸還是另有隱情,我是刑警寧澤膏燃,帶...
    沈念sama閱讀 35,735評(píng)論 5 346
  • 正文 年R本政府宣布茂卦,位于F島的核電站,受9級(jí)特大地震影響蹄梢,放射性物質(zhì)發(fā)生泄漏疙筹。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,354評(píng)論 3 330
  • 文/蒙蒙 一禁炒、第九天 我趴在偏房一處隱蔽的房頂上張望而咆。 院中可真熱鬧,春花似錦幕袱、人聲如沸暴备。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,936評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)涯捻。三九已至,卻和暖如春望迎,著一層夾襖步出監(jiān)牢的瞬間障癌,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,054評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工辩尊, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留涛浙,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,224評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像轿亮,于是被迫代替她去往敵國(guó)和親疮薇。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,974評(píng)論 2 355