Home >Web Front-end >JS Tutorial >React high-order component example analysis
This article mainly gives you an in-depth understanding of React high-order components. I hope you will have a clearer understanding of React high-order components.
1. In React, higher-order component (HOC) is an advanced technology for reusing component logic. HOC is not part of the React API. A HOC is a function that takes a component and returns a new component. In React, components are the basic unit of code reuse.
2. In order to explain HOCs, take the following two examples
The CommentList component will render a comments list, and the data in the list comes from the outside.
class CommentList extends React.Component { constructor() { super(); this.handleChange = this.handleChange.bind(this); this.state = { // "DataSource" is some global data source comments: DataSource.getComments() }; } componentDidMount() { // Subscribe to changes DataSource.addChangeListener(this.handleChange); } componentWillUnmount() { // Clean up listener DataSource.removeChangeListener(this.handleChange); } handleChange() { // Update component state whenever the data source changes this.setState({ comments: DataSource.getComments() }); } render() { return ( <p> {this.state.comments.map((comment) => ( <Comment comment={comment} key={comment.id} /> ))} </p> ); } }
Next is the BlogPost component, which is used to display a blog message
class BlogPost extends React.Component { constructor(props) { super(props); this.handleChange = this.handleChange.bind(this); this.state = { blogPost: DataSource.getBlogPost(props.id) }; } componentDidMount() { DataSource.addChangeListener(this.handleChange); } componentWillUnmount() { DataSource.removeChangeListener(this.handleChange); } handleChange() { this.setState({ blogPost: DataSource.getBlogPost(this.props.id) }); } render() { return <TextBlock text={this.state.blogPost} />; } }
These two components are different, they call different methods of DataSource, and their output are different, but most of their implementations are the same:
1. After the loading is completed, a change listener is added to the DataSource
2. When the data source changes, inside the listener Call setState
3. After uninstalling, remove the change listener
It is conceivable that in large applications, the same pattern of accessing DataSource and calling setState will happen again and again. We want to abstract this process so that we only define this logic in one place and then share it across multiple components.
Next we write a function that creates a component. This function accepts two parameters, one of which is the component and the other is the function. The withSubscription function is called below
const CommentListWithSubscription = withSubscription( CommentList, (DataSource) => DataSource.getComments() ); const BlogPostWithSubscription = withSubscription( BlogPost, (DataSource, props) => DataSource.getBlogPost(props.id) );
The first parameter passed when calling withSubscription is the wrapped component, and the second parameter is a function, which is used to retrieve data.
When CommentListWithSubscription and BlogPostWithSubscription are rendered, CommentList and BlogPost will accept a prop called data, which stores the data currently retrieved from the DataSource. The withSubscription code is as follows:
// This function takes a component... function withSubscription(WrappedComponent, selectData) { // ...and returns another component... return class extends React.Component { constructor(props) { super(props); this.handleChange = this.handleChange.bind(this); this.state = { data: selectData(DataSource, props) }; } componentDidMount() { // ... that takes care of the subscription... DataSource.addChangeListener(this.handleChange); } componentWillUnmount() { DataSource.removeChangeListener(this.handleChange); } handleChange() { this.setState({ data: selectData(DataSource, this.props) }); } render() { // ... and renders the wrapped component with the fresh data! // Notice that we pass through any additional props return <WrappedComponent data={this.state.data} {...this.props} />; } }; }
HOC does not modify the input component, nor does it use inheritance to reuse its behavior. HOC is just a function. The wrapped component accepts all the props of the container, and also accepts a new prop (data), which is used to render the output of the wrapped component. HOC doesn't care how the data is used or why the data is used, and the wrapped component doesn't care where the data is obtained.
Because withSubscription is just a regular function, you can add any number of parameters. For example, you can make the name of the data prop configurable to further isolate the HOC from the wrapped component.
Either accept a configuration shouldComponentUpdate, or configure the parameters of the data source
There are some things that need to be paid attention to when using high-order components.
1. Do not modify the original component, this is very important
There are the following examples:
function logProps(InputComponent) { InputComponent.prototype.componentWillReceiveProps = function(nextProps) { console.log('Current props: ', this.props); console.log('Next props: ', nextProps); }; // The fact that we're returning the original input is a hint that it has // been mutated. return InputComponent; } // EnhancedComponent will log whenever props are received const EnhancedComponent = logProps(InputComponent);
There are some problems here, 1. The input component cannot be separated from the enhanced component Reuse. 2. If you apply other HOCs to EnhancedComponent, componentWillReceiveProps will also be changed.
This HOC is not applicable to function type components, because function type components do not have a life cycle. Function HOC should use composition instead of modification - by wrapping the input component into a container component.
function logProps(WrappedComponent) { return class extends React.Component { componentWillReceiveProps(nextProps) { console.log('Current props: ', this.props); console.log('Next props: ', nextProps); } render() { // Wraps the input component in a container, without mutating it. Good! return <WrappedComponent {...this.props} />; } } }
This new logProps has the same functionality as the old logProps, while the new logProps avoids potential conflicts. The same applies to class type components and function type components.
2. Don’t use HOCs in the render method
React’s diff algorithm uses the identity of the component to decide whether it should update the existing subtree or tear down the old subtree and load a new one , if the component returned from the render method is equal to the previously rendered component (===), then React will update the previously rendered component through the diff algorithm. If not, the previously rendered subtree will be completely unloaded.
render() { // A new version of EnhancedComponent is created on every render // EnhancedComponent1 !== EnhancedComponent2 const EnhancedComponent = enhance(MyComponent); // That causes the entire subtree to unmount/remount each time! return <EnhancedComponent />; }
Use HOCs outside the component definition so that the resulting component is only created once. In a few cases, you need to apply HOCs dynamically, you should do this in the life cycle function or constructor
3. Static methods must be copied manually
Sometimes in React It is very useful to define static methods on components. When you apply HOCs to a component, although the original component is wrapped in a container component, the returned new component will not have any static methods of the original component.
// Define a static method WrappedComponent.staticMethod = function() {/*...*/} // Now apply an HOC const EnhancedComponent = enhance(WrappedComponent); // The enhanced component has no static method typeof EnhancedComponent.staticMethod === 'undefined' // true
In order for the returned component to have the static methods of the original component, it is necessary to copy the static methods of the original component to the new component inside the function.
function enhance(WrappedComponent) { class Enhance extends React.Component {/*...*/} // Must know exactly which method(s) to copy :( // 你也能够借助第三方工具 Enhance.staticMethod = WrappedComponent.staticMethod; return Enhance; }
4. The ref on the container component will not be passed to the wrapped component
Although the props on the container component can be easily passed to the wrapped component, But the ref on the container component is not passed to the wrapped component. If you set a ref for a component returned through HOCs, this ref refers to the outermost container component, not the wrapped component.
Related recommendations
React high-order component entry example sharing
How to use Vue high-order component
Use a very simple example to understand the idea of react.js high-order components
The above is the detailed content of React high-order component example analysis. For more information, please follow other related articles on the PHP Chinese website!