Preface
Component reuse is a common requirement in React development. Sometimes, we need to share the state or behavior of a certain component with other components. Traditionally, it is implemented through Higher-Order Components (HOCs), but this approach sometimes brings some complexity and readability issues. Render Props provides a cleaner and more flexible way to share the state and behavior of components. This article will introduce in detail the concept, implementation methods and application scenarios of Render Props.
1.1Props details
props
It is normal and is passed in externally. The settings inside the component can also be initialized in some ways. The properties cannot be changed by the component itself, but you can pass in new ones through the parent component's active re-rendering method.props
React is very flexible, but it also has a strict rule:
All React components must protect their props from being changed like pure functions.
Pure function: input is certain, output is certain
In general, when using a component, you can put parameters in the attributes of the tag, and all attributes will be used as components.props
The key value of the object.
Components created by arrow functions need to be received through the function's parametersprops
Components created through classes need to be passedCome and receive
Components can refer to other components in their output.
This allows us to use the same component to abstract details at any level.
Buttons, forms, dialogs, and even the content of the entire screen: In React applications, these are usually represented as components.
1.2 Parent-child component communication
1.2.1 Building a parent-child component
src/
import React from 'react' import ReactDOM from 'react-dom/client' // When introduced, the suffix name can be omitted and can be configured in webpackimport App from './01-App-parent-child' const root = (('root')) (<App />)
src/
import React from 'react' // ? Why must the first letter of a component in react be capitalized?// If lowercase, it is considered as a tag inherent to html, and if html tag does not appear const Header = () => { return ( <header>react The core library focuses only on view layers</header> ) } class Content extends { render () { return ( <div>react 16.8 Launched react hooks</div> ) } } const Footer = () => { return ( <footer>reactIt's really simple</footer> ) } class App extends { render () { return ( <div> <Header></Header> <Content></Content> <Footer></Footer> </div> ) } } export default App
1.2.2 Parent component passes values to child component
src/
import React from 'react' import ReactDOM from 'react-dom/client' // When introduced, the suffix name can be omitted and can be configured in webpack// import App from './01-App-parent-child' import App from './02-App-parent-child-value' const root = (('root')) (<App />)
src/
import React from 'react' // The parent component adds a custom attribute where the child component is called. If the value of the attribute is a variable or boolean type,// Number type, object, array, null, undefined, function, need to use {} package // If the child component is a class component, inside the child component, the data passed by the parent component can be accessed through// If the child component is a functional component, the function has the default parameter to props, and the data passed by the parent component can be accessed through props const Header = (props) => { (props) // { name: '' } return ( <header>{ } The core library focuses only on view layers</header> ) } class Content extends { render () { () // {version: 16.8} return ( <div>react { } Launched react hooks</div> ) } } const Footer = ({ msg }) => { // Deconstructed msg from props object return ( <footer>reactReally very{ msg }</footer> ) } class App extends { render () { return ( <div> <Header name=""></Header> <Content version={ 16.8 }></Content> <Footer msg="Simple"></Footer> </div> ) } } export default App
1.2.3 The parent component sets the default value to the child component
src/
import React from 'react' import ReactDOM from 'react-dom/client' // When introduced, the suffix name can be omitted and can be configured in 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 = (('root')) (<App />)
src/
import React from 'react' // The parent component adds a custom attribute where the child component is called. If the value of the attribute is a variable or boolean type,// Number type, object, array, null, undefined, function, need to use {} package // If the child component is a class component, inside the child component, the data passed by the parent component can be accessed through// If the child component is a functional component, the function has the default parameter to props, and the data passed by the parent component can be accessed through props // If you need to set default values for child components// Whether it is a class component or a functional component, after defining the component, add the defaultProps property// If it is a class component, you can also set the default value through the static properties of the class const Header = (props) => { (props) // { name: '' } return ( <header>{ } The core library focuses only on view layers</header> ) } = { name: '' } class Content extends { static defaultProps = { // Static properties of the class version: 16.8 } render () { () // {version: 16.8} return ( <div>react { } Launched react hooks!</div> ) } } // = { // version: 16.8 // } const Footer = ({ msg }) => { // Deconstructed msg from props object return ( <footer>reactReally very{ msg }</footer> ) } = { msg: 'Simple' } class App extends { render () { return ( <div> {/* <Header name=""></Header> <Content version={ 16.8 }></Content> <Footer msg="Simple"></Footer> */} <Header></Header> <Content></Content> <Footer></Footer> </div> ) } } export default App
1.2.4 Verification using prop-types attribute
Since React v15.5,
Moved into another package. Please useprop-types libraryreplace.
$ cnpm i prop-types -D import PropTypes from 'prop-types'; = { // You can declare attributes as JS native type, by default// These properties are optional.optionalArray: , optionalBool: , optionalFunc: , optionalNumber: , optionalObject: , optionalString: , optionalSymbol: , // Any element that can be rendered (including numbers, strings, elements or arrays)// (or Fragment) also contains these types.optionalNode: , // A React element.optionalElement: , // A React element type (i.e., MyComponent).optionalElementType: , // You can also declare prop as an instance of the class, use it here// JS instanceof operator.optionalMessage: (Message), // You can make your prop only a specific value, specify it as// Enumeration type.optionalEnum: (['News', 'Photos']), // An object can be any of several typesoptionalUnion: ([ , , (Message) ]), // You can specify that an array is composed of elements of a certain typeoptionalArrayOf: (), // You can specify that an object consists of a value of a certain typeoptionalObjectOf: (), // You can specify that an object consists of a specific type valueoptionalObjectWithShape: ({ color: , fontSize: }), // An object with warnings on extra properties optionalObjectWithStrictShape: ({ name: , quantity: }), // You can add `isRequired` to any PropTypes property to ensure// When this prop is not provided, a warning message will be printed.requiredFunc: , // Required data of any typerequiredAny: , // You can specify a custom validator. It should return an Error object when validation fails.// Please do not use `` or throw exceptions, as this will not work in `oneOfType`.customProp: function(props, propName, componentName) { if (!/matchme/.test(props[propName])) { return new Error( 'Invalid prop `' + propName + '` supplied to' + ' `' + componentName + '`. Validation failed.' ); } }, // You can also provide a custom `arrayOf` or `objectOf` validator.// It should return an Error object when validation fails.// The validator will verify each value in the array or object. The first two parameters of the validator// The first one is the array or object itself// The second one is their current key.customArrayProp: (function(propValue, key, componentName, location, propFullName) { if (!/matchme/.test(propValue[key])) { return new Error( 'Invalid prop `' + propFullName + '` supplied to' + ' `' + componentName + '`. Validation failed.' ); } }) };
src/
import React from 'react' import ReactDOM from 'react-dom/client' // When introduced, the suffix name can be omitted and can be configured in 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 = (('root')) (<App />)
src/
import React from 'react' import PropTypes from 'prop-types' // The parent component adds a custom attribute where the child component is called. If the value of the attribute is a variable or boolean type,// Number type, object, array, null, undefined, function, need to use {} package // If the child component is a class component, inside the child component, the data passed by the parent component can be accessed through// If the child component is a functional component, the function has the default parameter to props, and the data passed by the parent component can be accessed through props // If you need to set default values for child components// Whether it is a class component or a functional component, after defining the component, add the defaultProps property// If it is a class component, you can also set the default value through the static properties of the class // If you need to verify the data type of the data passed by the parent component// It needs to be completed through the third-party module prop-types// Whether it is a class component or a functional component, the type verification is completed after defining the component.// Completed through component.propTypes, written as an object// The key value is the custom attribute name set when the parent component calls the child component// The value is PropTypes.Data type // If the custom property value must be passed, it will be done through PropTypes.data type.isRequired// If the custom attribute value can be of type number or type stirng,// Set by ([ , ]) const Header = (props) => { (props) // { name: '' } return ( <header>{ } The core library focuses only on view layers</header> ) } = { name: '' } = { // The first letter is not capitalized // name: name: } class Content extends { static defaultProps = { // Static properties of the class version: 16.8 } render () { () // {version: 16.8} return ( <div>react { } Launched react hooks!</div> ) } } // = { // version: 16.8 // } = { // version: version: ([ , ]) } const Footer = ({ msg }) => { // Deconstructed msg from props object return ( <footer>reactReally very{ msg }</footer> ) } = { msg: 'Simple' } = { // msg: // Invalid prop `msg` of type `string` supplied to `Footer`, expected `boolean` msg: } class App extends { render () { return ( <div> {/* <Header name=""></Header> <Content version={ 16.8 }></Content> <Footer msg="Simple"></Footer> */} <Header></Header> <Content></Content> <Footer></Footer> </div> ) } } export default App
1.3
We know that when using components, they can be nested. To use nested structures in custom components, you need to use 。
Equivalent to slot in vue
src/
import React from 'react' import ReactDOM from 'react-dom/client' // When introduced, the suffix name can be omitted and can be configured in 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 = (('root')) (<App />)
src/
import React from 'react' const Header = (props) => { return ( <header>1 { }</header> ) } class Content extends { render () { return ( <div>2 { }</div> ) } } const Footer = ({ children }) => { return ( <footer>3 { children }</footer> ) } class App extends { render () { return ( <div> <Header>react The core library focuses only on view layers</Header> <Content>react 16.8 Launched react hooks</Content> <Footer>reactIt's really simple</Footer> </div> ) } } export default App
If you need to add multiple elements to the component and display them in multiple locations, you can set it as follows:
src/
import React from 'react' import ReactDOM from 'react-dom/client' // When introduced, the suffix name can be omitted and can be configured in 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 = (('root')) (<App />)
src/
import React from 'react' // Use named slots in vue (<slot name=""></slot>)// react needs to rely on the subscript ofconst Header = (props) => { (props) return ( <header> <div>Output here1Value of { [0] }</div> <div>Output here2Value of { [1] }</div> <div>Output here3Value of { [2] }</div> </header> ) } class App extends { render () { return ( <div> <Header> <div>1111111</div> <div>2222222</div> <div>3333333</div> </Header> </div> ) } } export default App
Implementing a named slot similar to vue, you need to access it through the subscript of
1.4 render props feature
Use Render Props to solve Cross-Cutting Concerns
Components are the main unit of React code reuse, but it is not always obvious how to share the state or behavior of one component encapsulated to other components that require the same state.
The following components track mouse locations in a web application:
src/
import React from 'react' import ReactDOM from 'react-dom/client' // When introduced, the suffix name can be omitted and can be configured in 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 = (('root')) (<App />)
src/
There is no learning state state and event processing yet, so use it first
import { Component } from 'react' // The react event needs to be used onMouseMove// Native js onmounsemove// react event meets two conditions: the first must be an event, and the second this points to the current componentclass App extends Component { // The initialization state of react class component, similar to data in vue state = { x: 0, y: 0 } render() { return ( <div style={ { width: '100vw', height: '100vh', backgroundColor: '#f66'} } onMouseMove = { (event) => { (event) // Modify the initialization value ({ x: , y: }) } }> <p> The current mouse position is,x:{ },y: { } </p> </div> ) } } export default App;
When the cursor moves on the screen, the component is
<p>
Its coordinates are displayed in .The question now is: How do we reuse this behavior in another component? To put it another way, if another component needs to know the mouse location, can we encapsulate this behavior so that we can easily share it with other components?
render prop is a function prop that tells the component what content it needs to render.
src/
import React from 'react' import ReactDOM from 'react-dom/client' // When introduced, the suffix name can be omitted and can be configured in 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 = (('root')) (<App />)
src/
import { Component } from 'react' // Rendering property sharing component status// On the component (Mouse) that needs to be shared, add a render custom property, which is a custom function// Return the component (Cat) that needs to be shared with within the custom function (Cat)// Inside the component (Mouse) that needs to be shared, it can be called through () or (), and the parameters are the status that needs to be shared.// Then, within the function that defines the custom render attribute, a parameter will be received, and the parameter will be passed through the returned component (Cat) const Cat = ({ mounsePointer }) => { return ( <div style={ { position: 'fixed', left: , top: , 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) => { (event) // Modify the initialization value ({ x: , y: }) } }> <p> The current mouse position is,x:{ },y: { } </p> { () } </div> ) } } class App extends Component { render () { return ( <div> {/* <Mounse></Mounse> <Cat ></Cat> */} <Mounse render = { (mounsePointer) => { return <Cat mounsePointer = { mounsePointer }/> } }></Mounse> </div> ) } } export default App
This case actually completes the child component passing values to the parent component in react
Summarize
With Render Props, we can easily share the state or behavior of components with other components, thereby improving code reusability and maintainability. This article shows how to use Render Props to implement state sharing through a specific example - the mouse position tracking component. I hope this article can help readers better understand and apply Render Props and improve the development efficiency of React applications.
This is the end of this article about the Props features and applications in React. For more information about React Props features, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!