React?中的Props特性及其應(yīng)用
前言
在 React 開發(fā)中,組件復(fù)用是一個常見的需求。有時候,我們需要將某個組件的狀態(tài)或行為共享給其他組件。傳統(tǒng)的做法是通過高階組件(Higher-Order Components, HOC)來實現(xiàn),但這種方式有時會帶來一些復(fù)雜性和可讀性問題。Render Props 提供了一種更簡潔、更靈活的方式來共享組件的狀態(tài)和行為。本文將詳細介紹 Render Props 的概念、實現(xiàn)方法及其應(yīng)用場景。
1.1Props詳解
props是正常是外部傳入的,組件內(nèi)部也可以通過一些方式來初始化的設(shè)置,屬性不能被組件自己更改,但是你可以通過父組件主動重新渲染的方式來傳入新的 props
React 非常靈活,但它也有一個嚴格的規(guī)則:
所有 React 組件都必須像純函數(shù)一樣保護它們的 props 不被更改。
純函數(shù):輸入一定,輸出一定確定
總的來說,在使用一個組件的時候,可以把參數(shù)放在標簽的屬性當中,所有的屬性都會作為組件 props 對象的鍵值。
通過箭頭函數(shù)創(chuàng)建的組件,需要通過函數(shù)的參數(shù)來接收props
通過類創(chuàng)建的組件,需要通過 this.props來接收
組件可以在其輸出中引用其他組件。
這就可以讓我們用同一組件來抽象出任意層次的細節(jié)。
按鈕,表單,對話框,甚至整個屏幕的內(nèi)容:在 React 應(yīng)用程序中,這些通常都會以組件的形式表示。
1.2 父子組件通信
1.2.1 構(gòu)建一個父子組件
src/index.js
import React from 'react'
import ReactDOM from 'react-dom/client'
?
// 引入時,后綴名可以省略,可以在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 固有的標簽,而html固有標簽如果沒有,則不顯示
?
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 App1.2.2 父組件給子組件傳值
src/index.js
import React from 'react'
import ReactDOM from 'react-dom/client'
?
// 引入時,后綴名可以省略,可以在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ù)擁有默認參數(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 App1.2.3 父組件給子組件傳值設(shè)置默認值
src/index.js
import React from 'react'
import ReactDOM from 'react-dom/client'
?
// 引入時,后綴名可以省略,可以在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ù)擁有默認參數(shù)為props,可以通過 props 訪問到父組件傳遞的數(shù)據(jù)
?
// 如果需要給子組件設(shè)置默認值
// 不管是類組件 還是 函數(shù)式組件,在定義組件之后,添加defaultProps屬性即可
// 如果是類組件,還可以通過 類的靜態(tài)屬性 設(shè)置默認值
?
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 App1.2.4 使用prop-types屬性驗證
自 React v15.5 起,
React.PropTypes已移入另一個包中。請使用 prop-types 庫 代替。
$ cnpm i prop-types -D
import PropTypes from 'prop-types';
?
MyComponent.propTypes = {
// 你可以將屬性聲明為 JS 原生類型,默認情況下
// 這些屬性都是可選的。
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,
?
// 一個 React 元素。
optionalElement: PropTypes.element,
?
// 一個 React 元素類型(即,MyComponent)。
optionalElementType: PropTypes.elementType,
?
// 你也可以聲明 prop 為類的實例,這里使用
// JS 的 instanceof 操作符。
optionalMessage: PropTypes.instanceOf(Message),
?
// 你可以讓你的 prop 只能是特定的值,指定它為
// 枚舉類型。
optionalEnum: PropTypes.oneOf(['News', 'Photos']),
?
// 一個對象可以是幾種類型中的任意一個類型
optionalUnion: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.instanceOf(Message)
]),
?
// 可以指定一個數(shù)組由某一類型的元素組成
optionalArrayOf: PropTypes.arrayOf(PropTypes.number),
?
// 可以指定一個對象由某一類型的值組成
optionalObjectOf: PropTypes.objectOf(PropTypes.number),
?
// 可以指定一個對象由特定的類型值組成
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` ,確保
// 這個 prop 沒有被提供時,會打印警告信息。
requiredFunc: PropTypes.func.isRequired,
?
// 任意類型的必需數(shù)據(jù)
requiredAny: PropTypes.any.isRequired,
?
// 你可以指定一個自定義驗證器。它在驗證失敗時應(yīng)返回一個 Error 對象。
// 請不要使用 `console.warn` 或拋出異常,因為這在 `oneOfType` 中不會起作用。
customProp: function(props, propName, componentName) {
if (!/matchme/.test(props[propName])) {
return new Error(
'Invalid prop `' + propName + '` supplied to' +
' `' + componentName + '`. Validation failed.'
);
}
},
?
// 你也可以提供一個自定義的 `arrayOf` 或 `objectOf` 驗證器。
// 它應(yīng)該在驗證失敗時返回一個 Error 對象。
// 驗證器將驗證數(shù)組或?qū)ο笾械拿總€值。驗證器的前兩個參數(shù)
// 第一個是數(shù)組或?qū)ο蟊旧?
// 第二個是他們當前的鍵。
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'
?
// 引入時,后綴名可以省略,可以在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ù)擁有默認參數(shù)為props,可以通過 props 訪問到父組件傳遞的數(shù)據(jù)
?
// 如果需要給子組件設(shè)置默認值
// 不管是類組件 還是 函數(shù)式組件,在定義組件之后,添加defaultProps屬性即可
// 如果是類組件,還可以通過 類的靜態(tài)屬性 設(shè)置默認值
?
// 如果需要驗證父組件傳遞的數(shù)據(jù)的 數(shù)據(jù)類型
// 需要通過第三方模塊 prop-types 完成
// 不管是類組件還是函數(shù)式組件,都是在定義組件之后,完成類型的校驗
// 通過 組件.propTypes 完成,寫法為對象
// key值為 父組件調(diào)用子組件的時候設(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 App1.3 props.children
我們知道使用組件的時候,可以嵌套。要在自定義組件中使用嵌套結(jié)構(gòu),就需要使用 props.children 。
等同于 vue中的 slot 插槽
src/index.js
import React from 'react'
import ReactDOM from 'react-dom/client'
?
// 引入時,后綴名可以省略,可以在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如果需要給組件添加多個元素,并且顯示在多個位置,可以如下設(shè)置:
src/index.js
import React from 'react'
import ReactDOM from 'react-dom/client'
?
// 引入時,后綴名可以省略,可以在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 的下標
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實現(xiàn)類似vue的具名插槽,需要通過 props.children 的下標去訪問
1.4 render props特性
使用 Render Props 來解決橫切關(guān)注點(Cross-Cutting Concerns)
組件是 React 代碼復(fù)用的主要單元,但如何將一個組件封裝的狀態(tài)或行為共享給其他需要相同狀態(tài)的組件并不總是顯而易見。
以下組件跟蹤 Web 應(yīng)用程序中的鼠標位置:
src/index.js
import React from 'react'
import ReactDOM from 'react-dom/client'
?
// 引入時,后綴名可以省略,可以在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é)習狀態(tài)state以及事件處理,這里先用
import { Component } from 'react'
?
// react的事件需要使用 小駝峰 onMouseMove
// 原生js onmounsemove
// react 事件滿足兩個條件: 第一必須是事件,第二this指向當前的組件
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>
當前鼠標的位置在,x:{ this.state.x },y: { this.state.y }
</p>
</div>
)
}
}
export default App;當光標在屏幕上移動時,組件在
<p>中顯示其坐標。現(xiàn)在的問題是:我們?nèi)绾卧诹硪粋€組件中復(fù)用這個行為?換個說法,若另一個組件需要知道鼠標位置,我們能否封裝這一行為,以便輕松地與其他組件共享它?
render prop 是一個用于告知組件需要渲染什么內(nèi)容的函數(shù) prop。
src/index.js
import React from 'react'
import ReactDOM from 'react-dom/client'
?
// 引入時,后綴名可以省略,可以在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)上,添加一個render的自定義屬性,該屬性是一個自定義函數(shù)
// 在自定義函數(shù)的內(nèi)部返回需要共享給的那個組件(Cat)
// 在需要共享的組件(Mouse)內(nèi)部,通過 this.props.render() 或者 props.render() 即可調(diào)用,參數(shù)即為需要共享的狀態(tài)
// 那么在定義自定義render屬性的函數(shù)內(nèi)部,就會接收到 參數(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>
當前鼠標的位置在,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此案例實際上完成了react中子組件給父組件傳值
總結(jié)
通過 Render Props,我們可以將組件的狀態(tài)或行為輕松地共享給其他組件,從而提高代碼的復(fù)用性和可維護性。本文通過一個具體的例子——鼠標位置跟蹤組件,展示了如何使用 Render Props 實現(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 css-in-js基礎(chǔ)介紹與應(yīng)用
隨著React、Vue等支持組件化的MVVM前端框架越來越流行,在js中直接編寫css的技術(shù)方案也越來越被大家所接受。為什么前端開發(fā)者們更青睞于這些css-in-js的方案呢,下面帶你了解它2022-09-09
react拖拽組件react-sortable-hoc的使用
本文主要介紹了react拖拽組件react-sortable-hoc的使用,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習或者工作具有一定的參考學(xué)習價值,需要的朋友們下面隨著小編來一起學(xué)習學(xué)習吧2023-02-02
React中hook函數(shù)與useState及useEffect的使用
這篇文章主要介紹了React中hook函數(shù)與useState及useEffect的使用,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習或者工作具有一定的參考學(xué)習價值,需要的朋友們下面隨著小編來一起學(xué)習吧2022-10-10
從零開始搭建webpack+react開發(fā)環(huán)境的詳細步驟
這篇文章主要介紹了從零開始搭建webpack+react開發(fā)環(huán)境的詳細步驟,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-05-05
ReactiveCocoa代碼實踐之-UI組件的RAC信號操作
這篇文章主要介紹了ReactiveCocoa代碼實踐之-UI組件的RAC信號操作 的相關(guān)資料,需要的朋友可以參考下2016-04-04
react實現(xiàn)antd線上主題動態(tài)切換功能
這篇文章主要介紹了react實現(xiàn)antd線上主題動態(tài)切換功能,本文給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下2019-08-08
TypeScript在React中的應(yīng)用技術(shù)實例解析
這篇文章主要為大家介紹了TypeScript在React中的應(yīng)用技術(shù)實例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-04-04
React+Redux實現(xiàn)簡單的待辦事項列表ToDoList
這篇文章主要為大家詳細介紹了React+Redux實現(xiàn)簡單的待辦事項列表ToDoList,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-09-09

