React?中的Props特性及其應(yīng)用
前言
在 React 開發(fā)中,組件復(fù)用是一個(gè)常見的需求。有時(shí)候,我們需要將某個(gè)組件的狀態(tài)或行為共享給其他組件。傳統(tǒng)的做法是通過高階組件(Higher-Order Components, HOC)來實(shí)現(xiàn),但這種方式有時(shí)會(huì)帶來一些復(fù)雜性和可讀性問題。Render Props 提供了一種更簡潔、更靈活的方式來共享組件的狀態(tài)和行為。本文將詳細(xì)介紹 Render Props 的概念、實(shí)現(xiàn)方法及其應(yīng)用場景。
1.1Props詳解
props
是正常是外部傳入的,組件內(nèi)部也可以通過一些方式來初始化的設(shè)置,屬性不能被組件自己更改,但是你可以通過父組件主動(dòng)重新渲染的方式來傳入新的 props
React 非常靈活,但它也有一個(gè)嚴(yán)格的規(guī)則:
所有 React 組件都必須像純函數(shù)一樣保護(hù)它們的 props 不被更改。
純函數(shù):輸入一定,輸出一定確定
總的來說,在使用一個(gè)組件的時(shí)候,可以把參數(shù)放在標(biāo)簽的屬性當(dāng)中,所有的屬性都會(huì)作為組件 props
對象的鍵值。
通過箭頭函數(shù)創(chuàng)建的組件,需要通過函數(shù)的參數(shù)來接收props
通過類創(chuàng)建的組件,需要通過 this.props
來接收
組件可以在其輸出中引用其他組件。
這就可以讓我們用同一組件來抽象出任意層次的細(xì)節(jié)。
按鈕,表單,對話框,甚至整個(gè)屏幕的內(nèi)容:在 React 應(yīng)用程序中,這些通常都會(huì)以組件的形式表示。
1.2 父子組件通信
1.2.1 構(gòu)建一個(gè)父子組件
src/index.js
import React from 'react' import ReactDOM from 'react-dom/client' ? // 引入時(shí),后綴名可以省略,可以在webpack中配置 import App from './01-App-parent-child' ? const root = ReactDOM.createRoot(document.getElementById('root')) ? root.render(<App />)
src/01-App-parent-child.jsx
import React from 'react' // ? 為什么react中組件的首字母必須大寫? // 如果小寫,被視為 html 固有的標(biāo)簽,而html固有標(biāo)簽如果沒有,則不顯示 ? const Header = () => { return ( <header>react 核心庫只關(guān)注于視圖層</header> ) } ? class Content extends React.Component { render () { return ( <div>react 16.8 推出了 react hooks</div> ) } } ? const Footer = () => { return ( <footer>react真的很簡單</footer> ) } ? class App extends React.Component { render () { return ( <div> <Header></Header> <Content></Content> <Footer></Footer> </div> ) } } ? export default App
1.2.2 父組件給子組件傳值
src/index.js
import React from 'react' import ReactDOM from 'react-dom/client' ? // 引入時(shí),后綴名可以省略,可以在webpack中配置 // import App from './01-App-parent-child' import App from './02-App-parent-child-value' ? const root = ReactDOM.createRoot(document.getElementById('root')) ? root.render(<App />)
src/02-App-parent-child-value.jsx
import React from 'react' // 父組件在調(diào)用子組件的地方,添加自定義的屬性,如果屬性的值是變量,boolean類型, // number類型,對象,數(shù)組,null,undefined, 函數(shù),需要使用 {} 包裹 ? ? // 如果子組件是類組件,在子組件的內(nèi)部,可以通過 this.props 訪問到父組件傳遞的數(shù)據(jù) // 如果子組件是函數(shù)式組件,函數(shù)擁有默認(rèn)參數(shù)為props,可以通過 props 訪問到父組件傳遞的數(shù)據(jù) ? const Header = (props) => { console.log(props) // { name: 'React.js' } return ( <header>{ props.name } 核心庫只關(guān)注于視圖層</header> ) } ? class Content extends React.Component { render () { console.log(this.props) // {version: 16.8} return ( <div>react { this.props.version } 推出了 react hooks</div> ) } } ? const Footer = ({ msg }) => { // 從props對象解構(gòu)了msg return ( <footer>react真的很{ msg }</footer> ) } ? class App extends React.Component { render () { return ( <div> <Header name="React.js"></Header> <Content version={ 16.8 }></Content> <Footer msg="簡單"></Footer> </div> ) } } ? export default App
1.2.3 父組件給子組件傳值設(shè)置默認(rèn)值
src/index.js
import React from 'react' import ReactDOM from 'react-dom/client' ? // 引入時(shí),后綴名可以省略,可以在webpack中配置 // import App from './01-App-parent-child' // import App from './02-App-parent-child-value' import App from './03-App-parent-child-value-default' ? const root = ReactDOM.createRoot(document.getElementById('root')) ? root.render(<App />)
src/03-App-parent-child-value-default.jsx
import React from 'react' // 父組件在調(diào)用子組件的地方,添加自定義的屬性,如果屬性的值是變量,boolean類型, // number類型,對象,數(shù)組,null,undefined, 函數(shù),需要使用 {} 包裹 ? // 如果子組件是類組件,在子組件的內(nèi)部,可以通過 this.props 訪問到父組件傳遞的數(shù)據(jù) // 如果子組件是函數(shù)式組件,函數(shù)擁有默認(rèn)參數(shù)為props,可以通過 props 訪問到父組件傳遞的數(shù)據(jù) ? // 如果需要給子組件設(shè)置默認(rèn)值 // 不管是類組件 還是 函數(shù)式組件,在定義組件之后,添加defaultProps屬性即可 // 如果是類組件,還可以通過 類的靜態(tài)屬性 設(shè)置默認(rèn)值 ? const Header = (props) => { console.log(props) // { name: 'React.js' } return ( <header>{ props.name } 核心庫只關(guān)注于視圖層</header> ) } Header.defaultProps = { name: 'React.js' } ? class Content extends React.Component { static defaultProps = { // 類的靜態(tài)屬性 version: 16.8 } render () { console.log(this.props) // {version: 16.8} return ( <div>react { this.props.version } 推出了 react hooks!</div> ) } } // Content.defaultProps = { // version: 16.8 // } ? const Footer = ({ msg }) => { // 從props對象解構(gòu)了msg return ( <footer>react真的很{ msg }</footer> ) } Footer.defaultProps = { msg: '簡單' } class App extends React.Component { render () { return ( <div> {/* <Header name="React.js"></Header> <Content version={ 16.8 }></Content> <Footer msg="簡單"></Footer> */} <Header></Header> <Content></Content> <Footer></Footer> </div> ) } } ? export default App
1.2.4 使用prop-types屬性驗(yàn)證
自 React v15.5 起,
React.PropTypes
已移入另一個(gè)包中。請使用 prop-types 庫 代替。
$ cnpm i prop-types -D import PropTypes from 'prop-types'; ? MyComponent.propTypes = { // 你可以將屬性聲明為 JS 原生類型,默認(rèn)情況下 // 這些屬性都是可選的。 optionalArray: PropTypes.array, optionalBool: PropTypes.bool, optionalFunc: PropTypes.func, optionalNumber: PropTypes.number, optionalObject: PropTypes.object, optionalString: PropTypes.string, optionalSymbol: PropTypes.symbol, ? // 任何可被渲染的元素(包括數(shù)字、字符串、元素或數(shù)組) // (或 Fragment) 也包含這些類型。 optionalNode: PropTypes.node, ? // 一個(gè) React 元素。 optionalElement: PropTypes.element, ? // 一個(gè) React 元素類型(即,MyComponent)。 optionalElementType: PropTypes.elementType, ? // 你也可以聲明 prop 為類的實(shí)例,這里使用 // JS 的 instanceof 操作符。 optionalMessage: PropTypes.instanceOf(Message), ? // 你可以讓你的 prop 只能是特定的值,指定它為 // 枚舉類型。 optionalEnum: PropTypes.oneOf(['News', 'Photos']), ? // 一個(gè)對象可以是幾種類型中的任意一個(gè)類型 optionalUnion: PropTypes.oneOfType([ PropTypes.string, PropTypes.number, PropTypes.instanceOf(Message) ]), ? // 可以指定一個(gè)數(shù)組由某一類型的元素組成 optionalArrayOf: PropTypes.arrayOf(PropTypes.number), ? // 可以指定一個(gè)對象由某一類型的值組成 optionalObjectOf: PropTypes.objectOf(PropTypes.number), ? // 可以指定一個(gè)對象由特定的類型值組成 optionalObjectWithShape: PropTypes.shape({ color: PropTypes.string, fontSize: PropTypes.number }), ? // An object with warnings on extra properties optionalObjectWithStrictShape: PropTypes.exact({ name: PropTypes.string, quantity: PropTypes.number }), ? // 你可以在任何 PropTypes 屬性后面加上 `isRequired` ,確保 // 這個(gè) prop 沒有被提供時(shí),會(huì)打印警告信息。 requiredFunc: PropTypes.func.isRequired, ? // 任意類型的必需數(shù)據(jù) requiredAny: PropTypes.any.isRequired, ? // 你可以指定一個(gè)自定義驗(yàn)證器。它在驗(yàn)證失敗時(shí)應(yīng)返回一個(gè) Error 對象。 // 請不要使用 `console.warn` 或拋出異常,因?yàn)檫@在 `oneOfType` 中不會(huì)起作用。 customProp: function(props, propName, componentName) { if (!/matchme/.test(props[propName])) { return new Error( 'Invalid prop `' + propName + '` supplied to' + ' `' + componentName + '`. Validation failed.' ); } }, ? // 你也可以提供一個(gè)自定義的 `arrayOf` 或 `objectOf` 驗(yàn)證器。 // 它應(yīng)該在驗(yàn)證失敗時(shí)返回一個(gè) Error 對象。 // 驗(yàn)證器將驗(yàn)證數(shù)組或?qū)ο笾械拿總€(gè)值。驗(yàn)證器的前兩個(gè)參數(shù) // 第一個(gè)是數(shù)組或?qū)ο蟊旧? // 第二個(gè)是他們當(dāng)前的鍵。 customArrayProp: PropTypes.arrayOf(function(propValue, key, componentName, location, propFullName) { if (!/matchme/.test(propValue[key])) { return new Error( 'Invalid prop `' + propFullName + '` supplied to' + ' `' + componentName + '`. Validation failed.' ); } }) };
src/index.js
import React from 'react' import ReactDOM from 'react-dom/client' ? // 引入時(shí),后綴名可以省略,可以在webpack中配置 // import App from './01-App-parent-child' // import App from './02-App-parent-child-value' // import App from './03-App-parent-child-value-default' import App from './04-App-parent-child-value-default-type' ? const root = ReactDOM.createRoot(document.getElementById('root')) ? root.render(<App />)
src/04-App-parent-child-value-default-type.jsx
import React from 'react' import PropTypes from 'prop-types' // 父組件在調(diào)用子組件的地方,添加自定義的屬性,如果屬性的值是變量,boolean類型, // number類型,對象,數(shù)組,null,undefined, 函數(shù),需要使用 {} 包裹 ? // 如果子組件是類組件,在子組件的內(nèi)部,可以通過 this.props 訪問到父組件傳遞的數(shù)據(jù) // 如果子組件是函數(shù)式組件,函數(shù)擁有默認(rèn)參數(shù)為props,可以通過 props 訪問到父組件傳遞的數(shù)據(jù) ? // 如果需要給子組件設(shè)置默認(rèn)值 // 不管是類組件 還是 函數(shù)式組件,在定義組件之后,添加defaultProps屬性即可 // 如果是類組件,還可以通過 類的靜態(tài)屬性 設(shè)置默認(rèn)值 ? // 如果需要驗(yàn)證父組件傳遞的數(shù)據(jù)的 數(shù)據(jù)類型 // 需要通過第三方模塊 prop-types 完成 // 不管是類組件還是函數(shù)式組件,都是在定義組件之后,完成類型的校驗(yàn) // 通過 組件.propTypes 完成,寫法為對象 // key值為 父組件調(diào)用子組件的時(shí)候設(shè)置的 自定義的屬性名 // value 值為 PropTypes.數(shù)據(jù)類型 ? // 如果自定義的屬性值是必須得傳遞的,那么通過 PropTypes.數(shù)據(jù)類型.isRequired 完成 // 如果自定義的屬性值即可以是 number類型,也可以是stirng類型, // 通過 PropTypes.oneOfType([ PropTypes.number, PropTypes.string ]) 設(shè)置 ? const Header = (props) => { console.log(props) // { name: 'React.js' } return ( <header>{ props.name } 核心庫只關(guān)注于視圖層</header> ) } Header.defaultProps = { name: 'React.js' } Header.propTypes = { // 首字母不大寫 // name: PropTypes.string name: PropTypes.string.isRequired } ? class Content extends React.Component { static defaultProps = { // 類的靜態(tài)屬性 version: 16.8 } render () { console.log(this.props) // {version: 16.8} return ( <div>react { this.props.version } 推出了 react hooks!</div> ) } } // Content.defaultProps = { // version: 16.8 // } ? Content.propTypes = { // version: PropTypes.number version: PropTypes.oneOfType([ PropTypes.number, PropTypes.string ]) } ? const Footer = ({ msg }) => { // 從props對象解構(gòu)了msg return ( <footer>react真的很{ msg }</footer> ) } Footer.defaultProps = { msg: '簡單' } Footer.propTypes = { // msg: PropTypes.bool // Invalid prop `msg` of type `string` supplied to `Footer`, expected `boolean` msg: PropTypes.string } class App extends React.Component { render () { return ( <div> {/* <Header name="React.js"></Header> <Content version={ 16.8 }></Content> <Footer msg="簡單"></Footer> */} <Header></Header> <Content></Content> <Footer></Footer> </div> ) } } ? export default App
1.3 props.children
我們知道使用組件的時(shí)候,可以嵌套。要在自定義組件中使用嵌套結(jié)構(gòu),就需要使用 props.children
。
等同于 vue中的 slot 插槽
src/index.js
import React from 'react' import ReactDOM from 'react-dom/client' ? // 引入時(shí),后綴名可以省略,可以在webpack中配置 // import App from './01-App-parent-child' // import App from './02-App-parent-child-value' // import App from './03-App-parent-child-value-default' // import App from './04-App-parent-child-value-default-type' import App from './05-App-props-children' ? const root = ReactDOM.createRoot(document.getElementById('root')) ? root.render(<App />) ?
src/05-App-props-children.jsx
import React from 'react' ? const Header = (props) => { return ( <header>1 { props.children }</header> ) } ? class Content extends React.Component { render () { return ( <div>2 { this.props.children }</div> ) } } ? const Footer = ({ children }) => { return ( <footer>3 { children }</footer> ) } ? class App extends React.Component { render () { return ( <div> <Header>react 核心庫只關(guān)注于視圖層</Header> <Content>react 16.8 推出了 react hooks</Content> <Footer>react真的很簡單</Footer> </div> ) } } ? export default App
如果需要給組件添加多個(gè)元素,并且顯示在多個(gè)位置,可以如下設(shè)置:
src/index.js
import React from 'react' import ReactDOM from 'react-dom/client' ? // 引入時(shí),后綴名可以省略,可以在webpack中配置 // import App from './01-App-parent-child' // import App from './02-App-parent-child-value' // import App from './03-App-parent-child-value-default' // import App from './04-App-parent-child-value-default-type' // import App from './05-App-props-children' import App from './06-App-mutiple-props-children' ? const root = ReactDOM.createRoot(document.getElementById('root')) ? root.render(<App />)
src/06-App-mutiple-props-children.jsx
import React from 'react' ? // vue中 使用 具名插槽(<slot name=""></slot>) // react 需要依靠 props.children 的下標(biāo) const Header = (props) => { console.log(props) return ( <header> <div>這里輸出1的值 { props.children[0] }</div> <div>這里輸出2的值 { props.children[1] }</div> <div>這里輸出3的值 { props.children[2] }</div> </header> ) } ? class App extends React.Component { render () { return ( <div> <Header> <div>1111111</div> <div>2222222</div> <div>3333333</div> </Header> </div> ) } } ? export default App
實(shí)現(xiàn)類似vue的具名插槽,需要通過 props.children 的下標(biāo)去訪問
1.4 render props特性
使用 Render Props 來解決橫切關(guān)注點(diǎn)(Cross-Cutting Concerns)
組件是 React 代碼復(fù)用的主要單元,但如何將一個(gè)組件封裝的狀態(tài)或行為共享給其他需要相同狀態(tài)的組件并不總是顯而易見。
以下組件跟蹤 Web 應(yīng)用程序中的鼠標(biāo)位置:
src/index.js
import React from 'react' import ReactDOM from 'react-dom/client' ? // 引入時(shí),后綴名可以省略,可以在webpack中配置 // import App from './01-App-parent-child' // import App from './02-App-parent-child-value' // import App from './03-App-parent-child-value-default' // import App from './04-App-parent-child-value-default-type' // import App from './05-App-props-children' // import App from './06-App-mutiple-props-children' import App from './07-App-mouse-tracker' ? const root = ReactDOM.createRoot(document.getElementById('root')) ? root.render(<App />) ?
src/07-App-mouse-tracker.jsx
還沒有學(xué)習(xí)狀態(tài)state以及事件處理,這里先用
import { Component } from 'react' ? // react的事件需要使用 小駝峰 onMouseMove // 原生js onmounsemove // react 事件滿足兩個(gè)條件: 第一必須是事件,第二this指向當(dāng)前的組件 class App extends Component { // react 類組件的初始化狀態(tài),類似于vue中的data state = { x: 0, y: 0 } ? render() { return ( <div style={ { width: '100vw', height: '100vh', backgroundColor: '#f66'} } onMouseMove = { (event) => { console.log(event) // 修改初始化值 this.setState({ x: event.clientX, y: event.clientY }) } }> <p> 當(dāng)前鼠標(biāo)的位置在,x:{ this.state.x },y: { this.state.y } </p> </div> ) } } export default App;
當(dāng)光標(biāo)在屏幕上移動(dòng)時(shí),組件在
<p>
中顯示其坐標(biāo)。現(xiàn)在的問題是:我們?nèi)绾卧诹硪粋€(gè)組件中復(fù)用這個(gè)行為?換個(gè)說法,若另一個(gè)組件需要知道鼠標(biāo)位置,我們能否封裝這一行為,以便輕松地與其他組件共享它?
render prop 是一個(gè)用于告知組件需要渲染什么內(nèi)容的函數(shù) prop。
src/index.js
import React from 'react' import ReactDOM from 'react-dom/client' ? // 引入時(shí),后綴名可以省略,可以在webpack中配置 // import App from './01-App-parent-child' // import App from './02-App-parent-child-value' // import App from './03-App-parent-child-value-default' // import App from './04-App-parent-child-value-default-type' // import App from './05-App-props-children' // import App from './06-App-mutiple-props-children' // import App from './07-App-mouse-tracker' import App from './08-App-render-props' ? const root = ReactDOM.createRoot(document.getElementById('root')) ? root.render(<App />)
src/08-App-render-props.jsx
import { Component } from 'react' // 渲染屬性共享組件的狀態(tài) // 在需要共享的組件(Mouse)上,添加一個(gè)render的自定義屬性,該屬性是一個(gè)自定義函數(shù) // 在自定義函數(shù)的內(nèi)部返回需要共享給的那個(gè)組件(Cat) // 在需要共享的組件(Mouse)內(nèi)部,通過 this.props.render() 或者 props.render() 即可調(diào)用,參數(shù)即為需要共享的狀態(tài) // 那么在定義自定義render屬性的函數(shù)內(nèi)部,就會(huì)接收到 參數(shù),通過返回的組件(Cat)傳遞該參數(shù)即可 ? const Cat = ({ mounsePointer }) => { return ( <div style={ { position: 'fixed', left: mounsePointer.x, top: mounsePointer.y, backgroundColor: '#ccc', width: 100, height: 100 } }></div> ) } ? class Mounse extends Component { state = { x: 0, y: 0 } ? render () { return ( <div style={ { width: '100vw', height: '100vh', backgroundColor: '#f66'} } onMouseMove = { (event) => { console.log(event) // 修改初始化值 this.setState({ x: event.clientX, y: event.clientY }) } }> <p> 當(dāng)前鼠標(biāo)的位置在,x:{ this.state.x },y: { this.state.y } ? </p> { this.props.render(this.state) } </div> ) } } class App extends Component { render () { return ( <div> {/* <Mounse></Mounse> <Cat ></Cat> */} <Mounse render = { (mounsePointer) => { return <Cat mounsePointer = { mounsePointer }/> } }></Mounse> </div> ) } } ? export default App
此案例實(shí)際上完成了react中子組件給父組件傳值
總結(jié)
通過 Render Props,我們可以將組件的狀態(tài)或行為輕松地共享給其他組件,從而提高代碼的復(fù)用性和可維護(hù)性。本文通過一個(gè)具體的例子——鼠標(biāo)位置跟蹤組件,展示了如何使用 Render Props 實(shí)現(xiàn)狀態(tài)共享。希望本文能夠幫助讀者更好地理解和應(yīng)用 Render Props,提升 React 應(yīng)用的開發(fā)效率。
到此這篇關(guān)于React 中的Props特性及其應(yīng)用的文章就介紹到這了,更多相關(guān)React Props特性內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用React-Router時(shí)出現(xiàn)的錯(cuò)誤及解決
這篇文章主要介紹了使用React-Router時(shí)出現(xiàn)的錯(cuò)誤及解決方案,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-03-03react中代碼塊輸出,代碼高亮顯示,帶行號(hào),能復(fù)制的問題
這篇文章主要介紹了react中代碼塊輸出,代碼高亮顯示,帶行號(hào),能復(fù)制的問題,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-09-09在?React?項(xiàng)目中全量使用?Hooks的方法
這篇文章主要介紹了在?React?項(xiàng)目中全量使用?Hooks,使用 Hooks 能為開發(fā)提升不少效率,但并不代表就要拋棄 Class Component,依舊還有很多場景我們還得用到它,本文給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-10-10簡談創(chuàng)建React Component的幾種方式
這篇文章主要介紹了創(chuàng)建React Component的幾種方式,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,,需要的朋友可以參考下2019-06-06React中映射一個(gè)嵌套數(shù)組實(shí)現(xiàn)demo
這篇文章主要為大家介紹了React中映射一個(gè)嵌套數(shù)組實(shí)現(xiàn)demo,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12