摘要:高階組件可以封裝公共邏輯,給當前組件傳遞方法屬性,添加生命周期鉤子等。二是基礎組件的靜態方法也會因為高階組件的包裹會丟失。如果在開發中確實遇到了必須使用它們,就一定要注意高階組件的這個問題并認真解決。
高階組件可以封裝公共邏輯,給當前組件傳遞方法屬性,添加生命周期鉤子等。
案例:
一個項目中有的頁面需要判斷所處環境,如果在移動端則正常顯示頁面,并向用戶提示當前頁面所處的移動端環境,如果不在移動端則顯示提示讓其在移動端打開。但是有的頁面又不需要這個判斷。
如果在每個頁面都寫一段判斷邏輯未免麻煩,因此可以借助高階組件來處理這部分邏輯。
先創建一個高階組件
// 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 () } } // 通過定義的中間組件將頁面所處環境通過props傳遞給基礎組件 const C = props => (該頁面只能在移動端查看,請掃描下方二維碼打開。假設這里有張二維碼) return C; } export default withEnvironment;
然后在基礎組件中使用
// src/pages/Demo01/index.jsx import React from "react"; import withEnvironment from "../../container/withEnvironment"; function Demo01(props) { return (你現在正在{props.envdesc}中訪問該頁面) } export default withEnvironment(Demo01);
最后將基礎組件渲染出來即可查看到效果。
// src/index.js import React from "react"; import { render } from "react-dom"; import Demo01 from "./pages/Demo01"; const root = document.querySelector("#root"); render(, root);
在上面這個例子中,我們將環境判斷的邏輯放在了高階組件中處理,以后只要需要判斷環境的頁面只需要在基礎組件中這樣執行即可。
export default withEnvironment(Demo01);
除此之外,我們在實際開發中還會遇到一個非常常見的需求,那就是在進入一個頁面時需要判斷登錄狀態,登錄狀態與非登錄狀態的不同顯示,登錄狀態之后角色的不同顯示都可以通過高階組件統一來處理這個邏輯,然后將登錄狀態,角色信息等傳遞給基礎組件。
// 大概的處理邏輯 import React from "react"; import $ from "jquery"; // 假設已經封裝了一個叫做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,說明已經登錄過并保存了相關信息 if (getCookie("uid")) { this.setState({ islogin: true, rule: getCookie("rule") || 0, loading: false }) } else { // 如果找不到uid,則嘗試自動登錄,先從kookie中查找是否保存了登錄賬號與密碼 const userinfo = getCookie("userinfo"); if (userinfo) { // 調用登錄接口 $.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;
與第一個例子相比,這個例子更加接近實際應用并且邏輯也更更加復雜。因此涉及到了異步數據,因此最好的方式是在中間組件的componentDidMount中來處理邏輯。并在render中根據不同的狀態決定不同的渲染結果。
我們需要根據實際情況合理的使用react創建組件的兩種方式。這一點至關重要。上面兩個例子個人認為還是比較典型的能代表大多數情況。
react-router中的高階組件我們在學習react的過程中,會逐漸的與高階組件打交道,react-router 中的 withRouter應該算是會最早接觸到的高階組件。我們在使用的時候就知道,通過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"; // 傳入基礎組件作為參數 const withRouter = (Component) => { // 創建中間組件 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,用于解決基礎組件因為高階組件的包裹而丟失靜態方法的問題 return hoistStatics(C, Component); } export default withRouter;
如果對于高階組件的例子你已經熟知,那么withRouter的源碼其實很容易理解。它做所的工作就僅僅只是把routeComponentProps傳入基礎組件而已。
另外還需要注意點是在該源碼中,解決了兩個因為高階組件帶來的問題,一個是經過高階組件包裹的組件在使用時無法通過ref正確獲取到對應的值。二是基礎組件的靜態方法也會因為高階組件的包裹會丟失。不過好在這段源碼已經給我們提供了對應的解決方案。因此如果我們在使用中需要處理這2點的話,按照這里的方式來做就可以了。
但是通常情況下,我們也很少會在自定義的組件中添加靜態方法和使用ref。如果在開發中確實遇到了必須使用它們,就一定要注意高階組件的這2個問題并認真解決。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/83704.html
摘要:前面有講到過很多頁面會在初始時驗證登錄狀態與用戶角色。這個時候就涉及到一個高階組件的嵌套使用。而每一個高階組件函數執行之后中所返回的組件,剛好可以作為下一個高階組件的參數繼續執行,而并不會影響基礎組件中所獲得的新能力。 前面有講到過很多頁面會在初始時驗證登錄狀態與用戶角色。我們可以使用高階組件來封裝這部分驗證邏輯。封裝好之后我們在使用的時候就可以如下: export default w...
摘要:在前端基礎進階八深入詳解函數的柯里化一文中,我有分享柯里化相關的知識。雖然說高階組件與柯里化都屬于比較難以理解的知識點,但是他們組合在一起使用時并沒有新增更多的難點。 可能看過我以前文章的同學應該會猜得到當我用New的方法來舉例學習高階組件時,接下來要分享的就是柯里化了。高階組件與函數柯里化的運用是非常能夠提高代碼逼格的技巧,如果你有剩余的精力,完全可以花點時間學習一下。 在前端基礎進...
摘要:創建一個普通函數因為的存在所以變成構造函數創建一個方法在方法中,創建一個中間實例對中間實例經過邏輯處理之后返回使用方法創建實例而恰好,高階組件的創建邏輯與使用,與這里的方法完全一致。因為方法其實就是構造函數的高階組件。 很多人寫文章喜歡把問題復雜化,因此當我學習高階組件的時候,查閱到的很多文章都給人一種高階組件高深莫測的感覺。但是事實上卻未必。 有一個詞叫做封裝。相信寫代碼這么久了,大...
閱讀 1377·2021-10-08 10:04
閱讀 2681·2021-09-22 15:23
閱讀 2724·2021-09-04 16:40
閱讀 1172·2019-08-29 17:29
閱讀 1492·2019-08-29 17:28
閱讀 2988·2019-08-29 14:02
閱讀 2221·2019-08-29 13:18
閱讀 838·2019-08-23 18:35