摘要:高階組件可以封裝公共邏輯,給當(dāng)前組件傳遞方法屬性,添加生命周期鉤子等。二是基礎(chǔ)組件的靜態(tài)方法也會(huì)因?yàn)楦唠A組件的包裹會(huì)丟失。如果在開發(fā)中確實(shí)遇到了必須使用它們,就一定要注意高階組件的這個(gè)問(wèn)題并認(rèn)真解決。
高階組件可以封裝公共邏輯,給當(dāng)前組件傳遞方法屬性,添加生命周期鉤子等。
案例:
一個(gè)項(xiàng)目中有的頁(yè)面需要判斷所處環(huán)境,如果在移動(dòng)端則正常顯示頁(yè)面,并向用戶提示當(dāng)前頁(yè)面所處的移動(dòng)端環(huán)境,如果不在移動(dòng)端則顯示提示讓其在移動(dòng)端打開。但是有的頁(yè)面又不需要這個(gè)判斷。
如果在每個(gè)頁(yè)面都寫一段判斷邏輯未免麻煩,因此可以借助高階組件來(lái)處理這部分邏輯。
先創(chuàng)建一個(gè)高階組件
// src/container/withEnvironment/index.jsx import React from "react"; const envs = { weixin: "微信", qq: "QQ", baiduboxapp: "手機(jī)百度", weibo: "微博", other: "移動(dòng)端" } function withEnvironment(BasicComponent) { const ua = navigator.userAgent; const isMobile = "ontouchstart" in document; let env = "other"; if (ua.match(/MicroMessenger/i)) { env = "weixin"; } if (ua.match(/weibo/i)) { env = "weibo"; } if (ua.match(/qq/i)) { env = "qq"; } if (ua.match(/baiduboxapp/i)) { env = "baiduboxapp" } // 不同邏輯下返回不同的中間組件 if (!isMobile) { return function () { return () } } // 通過(guò)定義的中間組件將頁(yè)面所處環(huán)境通過(guò)props傳遞給基礎(chǔ)組件 const C = props => (該頁(yè)面只能在移動(dòng)端查看,請(qǐng)掃描下方二維碼打開。假設(shè)這里有張二維碼) return C; } export default withEnvironment;
然后在基礎(chǔ)組件中使用
// src/pages/Demo01/index.jsx import React from "react"; import withEnvironment from "../../container/withEnvironment"; function Demo01(props) { return (你現(xiàn)在正在{props.envdesc}中訪問(wèn)該頁(yè)面) } export default withEnvironment(Demo01);
最后將基礎(chǔ)組件渲染出來(lái)即可查看到效果。
// src/index.js import React from "react"; import { render } from "react-dom"; import Demo01 from "./pages/Demo01"; const root = document.querySelector("#root"); render(, root);
在上面這個(gè)例子中,我們將環(huán)境判斷的邏輯放在了高階組件中處理,以后只要需要判斷環(huán)境的頁(yè)面只需要在基礎(chǔ)組件中這樣執(zhí)行即可。
export default withEnvironment(Demo01);
除此之外,我們?cè)趯?shí)際開發(fā)中還會(huì)遇到一個(gè)非常常見的需求,那就是在進(jìn)入一個(gè)頁(yè)面時(shí)需要判斷登錄狀態(tài),登錄狀態(tài)與非登錄狀態(tài)的不同顯示,登錄狀態(tài)之后角色的不同顯示都可以通過(guò)高階組件統(tǒng)一來(lái)處理這個(gè)邏輯,然后將登錄狀態(tài),角色信息等傳遞給基礎(chǔ)組件。
// 大概的處理邏輯 import React from "react"; import $ from "jquery"; // 假設(shè)已經(jīng)封裝了一個(gè)叫做getCookie的方法獲取cookie import { getCookie } from "cookie"; function withRule(BasicComponent) { return class C extends React.Component { state = { islogin: false, rule: -1, loading: true, error: null } componentDidMount() { // 如果能直接在cookie中找到uid,說(shuō)明已經(jīng)登錄過(guò)并保存了相關(guān)信息 if (getCookie("uid")) { this.setState({ islogin: true, rule: getCookie("rule") || 0, loading: false }) } else { // 如果找不到uid,則嘗試自動(dòng)登錄,先從kookie中查找是否保存了登錄賬號(hào)與密碼 const userinfo = getCookie("userinfo"); if (userinfo) { // 調(diào)用登錄接口 $.post("/api/login", { username: userinfo.username, password: userinfo.password }).then(resp => { this.setState({ islogin: true, rule: resp.rule, islogin: false }) }).catch(err => this.setState({ error: err.message })) } else { // 當(dāng)無(wú)法自動(dòng)登錄時(shí),你可以選擇在這里彈出登錄框,或者直接顯示未登錄頁(yè)面的樣式等都可以 } } } render() { const { islogin, rule, loading, error } = this.state; if (error) { return (登錄接口請(qǐng)求失敗!錯(cuò)誤信息為:{error}) } if (loading) { return (頁(yè)面加載中, 請(qǐng)稍后...) } return () } } } export default withRule;
與第一個(gè)例子相比,這個(gè)例子更加接近實(shí)際應(yīng)用并且邏輯也更更加復(fù)雜。因此涉及到了異步數(shù)據(jù),因此最好的方式是在中間組件的componentDidMount中來(lái)處理邏輯。并在render中根據(jù)不同的狀態(tài)決定不同的渲染結(jié)果。
我們需要根據(jù)實(shí)際情況合理的使用react創(chuàng)建組件的兩種方式。這一點(diǎn)至關(guān)重要。上面兩個(gè)例子個(gè)人認(rèn)為還是比較典型的能代表大多數(shù)情況。
react-router中的高階組件我們?cè)趯W(xué)習(xí)react的過(guò)程中,會(huì)逐漸的與高階組件打交道,react-router 中的 withRouter應(yīng)該算是會(huì)最早接觸到的高階組件。我們?cè)谑褂玫臅r(shí)候就知道,通過(guò)withRouter包裝的組件,我們可以在props中訪問(wèn)到location, router等對(duì)象,這正是withRouter通過(guò)高階組件的方式傳遞過(guò)來(lái)的。
import React, { Component } from "react"; import { withRouter } from "react-router"; class Home extends Component { componentDidMount() { const { router } = this.props; router.push("/"); } render() { return (...) } } export default withRouter(Home);
我們可以來(lái)看看在react-router v4中withRouter的源碼。
import React from "react"; import PropTypes from "prop-types"; import hoistStatics from "hoist-non-react-statics"; import Route from "./Route"; // 傳入基礎(chǔ)組件作為參數(shù) const withRouter = (Component) => { // 創(chuàng)建中間組件 const C = (props) => { const { wrappedComponentRef, ...remainingProps } = props; return (( // wrappedComponentRef 用來(lái)解決高階組件無(wú)法正確獲取到ref的問(wèn)題 )}/> ) } C.displayName = `withRouter(${Component.displayName || Component.name})`; C.WrappedComponent = Component; C.propTypes = { wrappedComponentRef: PropTypes.func } // hoistStatics類似于Object.assign,用于解決基礎(chǔ)組件因?yàn)楦唠A組件的包裹而丟失靜態(tài)方法的問(wèn)題 return hoistStatics(C, Component); } export default withRouter;
如果對(duì)于高階組件的例子你已經(jīng)熟知,那么withRouter的源碼其實(shí)很容易理解。它做所的工作就僅僅只是把routeComponentProps傳入基礎(chǔ)組件而已。
另外還需要注意點(diǎn)是在該源碼中,解決了兩個(gè)因?yàn)楦唠A組件帶來(lái)的問(wèn)題,一個(gè)是經(jīng)過(guò)高階組件包裹的組件在使用時(shí)無(wú)法通過(guò)ref正確獲取到對(duì)應(yīng)的值。二是基礎(chǔ)組件的靜態(tài)方法也會(huì)因?yàn)楦唠A組件的包裹會(huì)丟失。不過(guò)好在這段源碼已經(jīng)給我們提供了對(duì)應(yīng)的解決方案。因此如果我們?cè)谑褂弥行枰幚磉@2點(diǎn)的話,按照這里的方式來(lái)做就可以了。
但是通常情況下,我們也很少會(huì)在自定義的組件中添加靜態(tài)方法和使用ref。如果在開發(fā)中確實(shí)遇到了必須使用它們,就一定要注意高階組件的這2個(gè)問(wèn)題并認(rèn)真解決。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/83704.html
摘要:前面有講到過(guò)很多頁(yè)面會(huì)在初始時(shí)驗(yàn)證登錄狀態(tài)與用戶角色。這個(gè)時(shí)候就涉及到一個(gè)高階組件的嵌套使用。而每一個(gè)高階組件函數(shù)執(zhí)行之后中所返回的組件,剛好可以作為下一個(gè)高階組件的參數(shù)繼續(xù)執(zhí)行,而并不會(huì)影響基礎(chǔ)組件中所獲得的新能力。 前面有講到過(guò)很多頁(yè)面會(huì)在初始時(shí)驗(yàn)證登錄狀態(tài)與用戶角色。我們可以使用高階組件來(lái)封裝這部分驗(yàn)證邏輯。封裝好之后我們?cè)谑褂玫臅r(shí)候就可以如下: export default w...
摘要:在前端基礎(chǔ)進(jìn)階八深入詳解函數(shù)的柯里化一文中,我有分享柯里化相關(guān)的知識(shí)。雖然說(shuō)高階組件與柯里化都屬于比較難以理解的知識(shí)點(diǎn),但是他們組合在一起使用時(shí)并沒(méi)有新增更多的難點(diǎn)。 可能看過(guò)我以前文章的同學(xué)應(yīng)該會(huì)猜得到當(dāng)我用New的方法來(lái)舉例學(xué)習(xí)高階組件時(shí),接下來(lái)要分享的就是柯里化了。高階組件與函數(shù)柯里化的運(yùn)用是非常能夠提高代碼逼格的技巧,如果你有剩余的精力,完全可以花點(diǎn)時(shí)間學(xué)習(xí)一下。 在前端基礎(chǔ)進(jìn)...
摘要:創(chuàng)建一個(gè)普通函數(shù)因?yàn)榈拇嬖谒宰兂蓸?gòu)造函數(shù)創(chuàng)建一個(gè)方法在方法中,創(chuàng)建一個(gè)中間實(shí)例對(duì)中間實(shí)例經(jīng)過(guò)邏輯處理之后返回使用方法創(chuàng)建實(shí)例而恰好,高階組件的創(chuàng)建邏輯與使用,與這里的方法完全一致。因?yàn)榉椒ㄆ鋵?shí)就是構(gòu)造函數(shù)的高階組件。 很多人寫文章喜歡把問(wèn)題復(fù)雜化,因此當(dāng)我學(xué)習(xí)高階組件的時(shí)候,查閱到的很多文章都給人一種高階組件高深莫測(cè)的感覺(jué)。但是事實(shí)上卻未必。 有一個(gè)詞叫做封裝。相信寫代碼這么久了,大...
摘要:系列種優(yōu)化頁(yè)面加載速度的方法隨筆分類中個(gè)最重要的技術(shù)點(diǎn)常用整理網(wǎng)頁(yè)性能管理詳解離線緩存簡(jiǎn)介系列編寫高性能有趣的原生數(shù)組函數(shù)數(shù)據(jù)訪問(wèn)性能優(yōu)化方案實(shí)現(xiàn)的大排序算法一怪對(duì)象常用方法函數(shù)收集數(shù)組的操作面向?qū)ο蠛驮屠^承中關(guān)鍵詞的優(yōu)雅解釋淺談系列 H5系列 10種優(yōu)化頁(yè)面加載速度的方法 隨筆分類 - HTML5 HTML5中40個(gè)最重要的技術(shù)點(diǎn) 常用meta整理 網(wǎng)頁(yè)性能管理詳解 HTML5 ...
閱讀 1459·2021-10-08 10:04
閱讀 2797·2021-09-22 15:23
閱讀 2779·2021-09-04 16:40
閱讀 1234·2019-08-29 17:29
閱讀 1560·2019-08-29 17:28
閱讀 3044·2019-08-29 14:02
閱讀 2287·2019-08-29 13:18
閱讀 934·2019-08-23 18:35