01、介紹
React 高階組件也叫做 React HOC(High Order Component), 它是react中的高級技術(shù), 用來重用組件邏輯。
但高階組件本身并不是React API。它只是一種模式,這種模式是由react自身的組合性質(zhì)必然產(chǎn)生的。
那么在學(xué)習(xí)高階組件之前有一個(gè)概念我們必須清楚,就是高階函數(shù)。
02、高階函數(shù)概念:高階函數(shù)是一個(gè)函數(shù),它接收函數(shù)作為參數(shù)或?qū)⒑瘮?shù)作為輸出返回
舉個(gè)栗子:
接收函數(shù)作為參數(shù)
function a(x) { x(); } function b() { alert("hello"); } a(b);
將函數(shù)作為輸出返回
function a() { function b() { alert("hello"); } return b; } a()();
以上函數(shù)a就是一個(gè)高階函數(shù), 用法非常簡單, 那么實(shí)際開發(fā)中又有哪些是高階函數(shù)呢?
Array 的 map 、reduce 、filter 等方法
Object 的 keys 、values 等方法
03、高階組件概念:高階組件就是一個(gè)函數(shù),且該函數(shù)接受一個(gè)組件作為參數(shù),并返回一個(gè)新的組件
舉個(gè)栗子:
// WrappedComponent 就是傳入的包裝組件 function withHoc(WrappedComponent) { return class extends Component { render () { return () } } }
withHoc 函數(shù)就是一個(gè)高階組件。那么高階組件到底有什么神奇的魔力,值得我們?yōu)橹裕?/p>
開發(fā)組件時(shí),我們會(huì)遇到相同的功能,使用高階組件則能減少重復(fù)代碼
04、高階組件實(shí)訓(xùn)1目的: 定義高階組件
組件 Login -- 登陸頁面
// 受控組件 class Login extends Component { state = { username: "", password: "" } onUsernameChange = (e) => { this.setState({username: e.target.value}); } onPasswordChange = (e) => { this.setState({password: e.target.value}); } login = (e) => { // 禁止默認(rèn)事件 e.preventDefault(); // 收集表單數(shù)據(jù) const { username, password } = this.state; alert(`用戶名: ${username}, 密碼: ${password}`); } render () { const { username, password } = this.state; return () } }登陸
組件 Register -- 注冊頁面
// 受控組件 class Register extends Component { state = { username: "", password: "", rePassword: "" } onUsernameChange = (e) => { this.setState({username: e.target.value}); } onPasswordChange = (e) => { this.setState({password: e.target.value}); } onRePasswordChange = (e) => { this.setState({rePassword: e.target.value}); } register = (e) => { // 禁止默認(rèn)事件 e.preventDefault(); // 收集表單數(shù)據(jù) const { username, password, rePassword } = this.state; alert(`用戶名: ${username}, 密碼: ${password}, 確認(rèn)密碼: ${rePassword}`); } render () { const { username, password, rePassword } = this.state; return () } }注冊
頁面效果
我們發(fā)現(xiàn)里面重復(fù)邏輯實(shí)在太多了,尤其是 onXxxChange 函數(shù)出現(xiàn)太多,我們先優(yōu)化一下。
// 我們以 Register 組件為例來看 class Register extends Component { state = { username: "", password: "", rePassword: "" } // 最終修改狀態(tài)數(shù)據(jù)的函數(shù) onChange = (stateName, stateValue) => { this.setState({[stateName]: stateValue}); } // 高階函數(shù) --> 這樣后面就能一直復(fù)用當(dāng)前函數(shù),而不用重新創(chuàng)建了~ composeChange = (name) => { return (e) => this.onChange(name, e.target.value); } // 統(tǒng)一所有提交表單函數(shù)名 handleSubmit = (e) => { e.preventDefault(); const { username, password, rePassword } = this.state; alert(`用戶名: ${username}, 密碼: ${password}, 確認(rèn)密碼: ${rePassword}`); } render () { const { username, password, rePassword } = this.state; return () } }注冊
現(xiàn)在兩個(gè)頁面都有 onChange 、 composeChange 、handleSubmit 函數(shù)和相關(guān)的狀態(tài),我們接下來提取,封裝成高階組件!
// 高階組件 withHoc export default function withHoc(WrappedComponent) { return class extends Component { state = { username: "", password: "", rePassword: "" } onChange = (stateName, stateValue) => { this.setState({[stateName]: stateValue}); } composeChange = (name) => { return (e) => this.onChange(name, e.target.value); } handleSubmit = (e) => { e.preventDefault(); const { username, password, rePassword } = this.state; if (rePassword) { alert(`用戶名: ${username}, 密碼: ${password}, 確認(rèn)密碼: ${rePassword}`); } else { alert(`用戶名: ${username}, 密碼: ${password}`); } } render () { // 抽取方法 const mapMethodToProps = { composeChange: this.composeChange, handleSubmit: this.handleSubmit, } // 將狀態(tài)數(shù)據(jù)和操作的方法以 props 的方式傳入的包裝組件中 return () } } // 向外暴露的是高階組件的返回值~包裝了 Register 組件返回了一個(gè)新組件 export default withHoc(Register);{/*提取公共頭部*/}) } } } // 組件 Register class Register extends Component { render () { const { handleSubmit, composeChange, username, password, rePassword } = this.props; return (xxx
現(xiàn)在我們提取了公共方法、狀態(tài)等數(shù)據(jù), 封裝了一個(gè)基本的高階組件。 但是還有很多需要問題需要解決,現(xiàn)在開始行動(dòng)~
05、高階組件實(shí)訓(xùn)2目的: 向高階組件中傳參
修改高階組件
// 再次包裹了一層高階函數(shù), 這個(gè)高階函數(shù)執(zhí)行后返回值才是高階組件 // 通過這種方式, 高階組件內(nèi)部就能獲取參數(shù)了~ export default (title) => (WrappedComponent) => { return class Form extends Component { ...重復(fù)代碼省略... render () { const mapMethodToProps = { composeChange: this.composeChange, handleSubmit: this.handleSubmit, } return ({/*獲取到參數(shù)值就能正常顯示了~*/}) } } }{title}
在 Login / Register 組件中使用
export default withHoc("登陸")(Login);
export default withHoc("注冊")(Register);
06、高階組件實(shí)訓(xùn)3目的: 獲取父組件傳遞的 props
修改 App 組件
class App extends Component { render() { return ({/*父組件向子組件傳遞屬性*/}); } }
修改高階組件
export default (title) => (WrappedComponent) => { return class Form extends Component { ...重復(fù)代碼省略... render () { const mapMethodToProps = { composeChange: this.composeChange, handleSubmit: this.handleSubmit, } return ({/*獲取到參數(shù)值就能正常顯示了~*/}) } } }{title}
{/* 將當(dāng)前組件接受到的props傳給包裝組件~*/}
Login 組件中使用
class Login extends Component { render () { const { handleSubmit, composeChange, username, password, name, age } = this.props; return (07、高階組件實(shí)訓(xùn)4) } }你的名字: {name}
你的年齡: {age}
目的: 修改在 React-devtool 中高階組件名稱,方便調(diào)試
修改高階組件
export default (title) => (WrappedComponent) => { return class Form extends Component { // 定義靜態(tài)方法,修改組件在調(diào)試工具中顯示的名稱 static displayName = `Form(${getDisplayName(WrappedComponent)})` ...省略重復(fù)代碼... } } // 獲取包裝組件的displayName的方法 function getDisplayName(WrappedComponent) { return WrappedComponent.displayName || WrappedComponent.name || "Component"; }
修改之前名稱
修改之后名稱
08、使用裝飾器目的: 簡化使用高階組件
下載包
npm i react-app-rewired customize-cra @babel/plugin-proposal-decorators -D
在項(xiàng)目根目錄配置 config-overrides.js
const { override, addDecoratorsLegacy } = require("customize-cra"); // 修改 create-react-app 的 webpack 的配置 module.exports = override( addDecoratorsLegacy() )
修改 package.json 的 scripts
// 將 react-scripts 修改為 react-app-rewired "scripts": { "start": "react-app-rewired start", "build": "react-app-rewired build", "test": "react-app-rewired test" },
以上就是使用 decorator 的配置,修改完后就能使用了~
修改 Login 組件
@withHoc("登陸") class Login extends Component { ...省略重復(fù)代碼... } export default Login;
修改 Register 組件
@withHoc("注冊") class Register extends Component { ...省略重復(fù)代碼... } export default Register;
react-app-rewired customize-cra 是 create-react-app 2.0以上專門用來修改 webpack 的配置
decorator 還能做很多事,感興趣朋友可以看看 阮一峰ES6教程 了解更多
重復(fù)代碼永遠(yuǎn)是我們需要考慮處理的代碼,所以我們有模塊化、組件化、工具類函數(shù)等等,
在 React 中再次引入了一個(gè)高階組件的概念,都是為了去除掉萬惡的重復(fù)代碼,讓我們代碼變得更加精簡。
本篇文章所有源碼都放在了 git倉庫,如果它對你有幫助的話,歡迎點(diǎn) star ~~
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/102900.html
摘要:單頁博客應(yīng)用編寫總結(jié)很久之前就想寫一個(gè)博客應(yīng)用在一開始想要直接用和模板直接寫但是暑假一開始的時(shí)候不小心入了的坑所以就一不做二不休直接用寫那既然用了不寫個(gè)單頁應(yīng)用也過意不去了不前前后后寫了將近兩個(gè)星期現(xiàn)在看來這其實(shí)是一個(gè)很容易的應(yīng)用但是鑒于 React-Express單頁博客應(yīng)用編寫總結(jié) 很久之前就想寫一個(gè)博客應(yīng)用.在一開始想要直接用express和ejs模板直接寫, 但是暑假一開始的時(shí)...
摘要:因?yàn)楣ぷ髦幸恢痹谑褂茫惨恢币詠硐肟偨Y(jié)一下自己關(guān)于的一些知識經(jīng)驗(yàn)。于是把一些想法慢慢整理書寫下來,做成一本開源免費(fèi)專業(yè)簡單的入門級別的小書,提供給社區(qū)。本書的后續(xù)可能會(huì)做成視頻版本,敬請期待。本作品采用署名禁止演繹國際許可協(xié)議進(jìn)行許可 React.js 小書 本文作者:胡子大哈本文原文:React.js 小書 轉(zhuǎn)載請注明出處,保留原文鏈接以及作者信息 在線閱讀:http://huzi...
摘要:前端每周清單半年盤點(diǎn)之與篇前端每周清單專注前端領(lǐng)域內(nèi)容,以對外文資料的搜集為主,幫助開發(fā)者了解一周前端熱點(diǎn)分為新聞熱點(diǎn)開發(fā)教程工程實(shí)踐深度閱讀開源項(xiàng)目巔峰人生等欄目。與求同存異近日,宣布將的構(gòu)建工具由遷移到,引發(fā)了很多開發(fā)者的討論。 前端每周清單半年盤點(diǎn)之 React 與 ReactNative 篇 前端每周清單專注前端領(lǐng)域內(nèi)容,以對外文資料的搜集為主,幫助開發(fā)者了解一周前端熱點(diǎn);分為...
摘要:但如果你想更加高效地使用來開發(fā),成為大師,那下面我要傳授的這五招你一定得認(rèn)真學(xué)習(xí)一下了。雖然損失了一丟丟性能,但避免了無限的。所以我們需要設(shè)置,這些默認(rèn)行為將會(huì)被去掉以上兩點(diǎn)的優(yōu)化才能成功。陸續(xù)可能還會(huì)更新一些別的招數(shù),敬請期待。 本文面向?qū)ο笫怯幸欢╒ue.js編程經(jīng)驗(yàn)的開發(fā)者。如果有人需要Vue.js入門系列的文章可以在評論區(qū)告訴我,有空就給你們寫。 對大部分人來說,掌握Vue.j...
閱讀 3436·2021-11-12 10:36
閱讀 2759·2021-11-11 16:55
閱讀 2981·2021-09-27 13:36
閱讀 1628·2021-08-05 10:01
閱讀 3570·2019-08-30 15:55
閱讀 784·2019-08-30 13:01
閱讀 1920·2019-08-29 17:16
閱讀 2389·2019-08-29 16:40