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