Three ways to share component logic in React

Three ways to share component logic in React

Without further ado, these three methods are: render props, higher-order components, and custom Hooks. The following demonstrates

Suppose there is a TimeOnPage component dedicated to recording the time a user stays on the current page, like this:

const TimeOnPage = () => {
 const [second, setSecond] = useState(0);

 useEffect(() => {
  setTimeout(() => {
   setSecond(second + 1);
  }, 1000);
 }, [second]);
 return (
  <div>Dwell time: {second} seconds</div>
 );
}

If another component needs to reuse this functionality, can we encapsulate it so it can be easily shared with other components?

It is natural to think of nested subcomponents and use props to pass parameters.

const Child = (props) => {
 return <div>stayTime: {props.stayTime}s</div>;
};

const TimeOnPage = () => {
 const [second, setSecond] = useState(0);

 useEffect(() => {
  setTimeout(() => {
   setSecond(second + 1);
  }, 1000);
 }, [second]);
 return (
  <div>
   <Child stayTime={second} />
  </div>
 );
}

This is hard-coded inside the TimeOnPage component and has not yet achieved the goal of encapsulation and reuse. See how render props works?

Render props

"render prop" refers to a simple technique for sharing code between React components using a prop whose value is a function.

Continuing from the previous article, define a prop with a function value in TimeOnPage. If you want to render a component, just return it in the function. The function parameter is the state you want to share.

const Child = (props) => {
 return <div>stayTime: {props.stayTime}s</div>;
};

const TimeOnPage = (props) => {
 const [second, setSecond] = useState(0);

 useEffect(() => {
  setTimeout(() => {
   setSecond(second + 1);
  }, 1000);
 }, [second]);
 return <div>{props.render(second)}</div>;
};

<TimeOnPage render={(stayTime) => <Child stayTime={stayTime} />

In fact, render prop is a function prop used to tell the component what content needs to be rendered.
React Router also uses this technique.

<Router>
 <Route path="/home" render={() => <div>Home</div>} />
</Router>

Higher-order components

Higher-order components (HOC) are an advanced technique for reusing component logic in React. HOC itself is not part of the React API, it is a design pattern based on the composition characteristics of React.

A high-order component is a function whose parameter is a component A that needs to be reused and whose return value is a new component N. The new component N is processed based on component A, but component A itself will not be modified, only the function is enhanced.

Suppose there is a news list component that looks like this:

const NewList = () => {
 return (
  <div>
   <ul>
    <li>news item</li>
    <li>news item</li>
   </ul>
  </div>
 );
}

If you want to display the loading animation component <Loading /> during the loading of the news list, you usually do this

const Loading = () => {
 // loading animation}
const NewList = ({ isLoading }) => {
 return isLoading ? (
  <Loading />
 ) : (
  <div>
   <ul>
    <li>news item</li>
    <li>news item</li>
   </ul>
  </div>
 );
};

Suppose now the Table component also wants to display the loading animation component during data loading, following a similar pattern

const Loading = () => {
 // loading animation}
const DataList = ({ isLoading, ...props }) => {
 return isLoading ? (
  <Loading />
 ) : (
  <Table {...props} />
 );
};

From the above, you will find that the structures of DataList and NewList are extremely similar. If there are third and fourth components to be loaded, do we continue to repeat this pattern for the third and fourth time? This is not the most ideal approach. A better approach is to use a higher-order component to abstract this pattern:

const WithLoading = (WrappedComponent) => {
 return ({isLoading, ...props}) => {
  return isLoading ? <Loading /> : <WrappedComponent {...props} />;
 }
};

Then you can add loading to them separately without modifying NewList and DataList

const NewList = () => {
 return (
  <div>
   <ul>
    <li>news item</li>
    <li>news item</li>
   </ul>
  </div>
 );
};

const DataList = (props) => {
 return <Table {...props} />
};

const WithLoading = (WrappedComponent) => {
 return ({isLoading, ...props}) => {
  return isLoading ? <Loading /> : <WrappedComponent {...props} />;
 }
};
// NewList with loading
const WithLoadingNewList = WithLoading(<NewList />)
// DataList with loading
const WithLoadingDataList = WithLoading(<DataList />)

Custom Hooks

Hooks are a new feature in React 16.8. It allows you to use state and other React features without writing classes.

React Hooks include useState, useEffect, etc. They are all functions. Custom Hook is also a function. Its name also starts with use. Other Hooks can be called inside the function. Unlike React components, custom Hooks do not have to return a value. Unlike ordinary functions, custom Hooks can call other Hooks, while ordinary functions cannot.

When writing business logic, some reusable methods are generally defined as tool functions, which can then be reused everywhere. Similarly, by customizing Hooks, you can extract component logic into reusable functions. Whether to choose a custom Hook or a tool function depends on whether the component logic to be extracted requires other Hooks. If so, choose a custom Hook, otherwise just use a tool function.

Go back to the first TimeOnPage component in this article and change it to a custom Hook

const useTimeOnPage = () => {
 const [second, setSecond] = useState(0);

 useEffect(() => {
  setTimeout(() => {
   setSecond(second + 1);
  }, 1000);
 }, [second]);
 return second;
}

How to use

const Demo = () => {
 const stayTime = useTimeOnPage();
 return <div>Current page stay time: {stayTime} seconds</div>
}

Summarize

The three ways of sharing component logic have their own applicable scenarios:
Render props are suitable for sharing parent components with different subcomponents/sub-elements. The "pits" of subcomponents/sub-elements have been defined and can only be rendered in the specified positions;
High-order components are suitable for extending components without modifying the original components;
Pure functions can basically do what custom Hooks can do, but sometimes it is more convenient and faster to use custom Hooks.
Link to this article: Github

This concludes this article on the three ways to share component logic in React. For more content about React shared component logic, please search for previous articles on 123WORDPRESS.COM or continue to browse the following related articles. I hope you will support 123WORDPRESS.COM in the future!

You may also be interested in:
  • React component refactoring: nesting + inheritance and high-order components explained
  • The communication process between nested components and nested components in React
  • Tips for writing concise React components
  • 5 things to note when writing React components using hooks
  • React implements the sample code of Radio component
  • Detailed explanation of how to implement login function by combining React with Antd's Form component
  • Example code for developing h5 form page based on react hooks and zarm component library configuration
  • React antd tabs switching causes repeated refresh of subcomponents
  • How to pass pop-up form content to parent component in react-antd
  • Use antd's form component in the react project to dynamically set the value of the input box
  • A brief talk about component logic reuse in React
  • React nested component construction order

<<:  MySQL 5.7.17 installation graphic tutorial (windows)

>>:  Detailed tutorial on installing PHP and Nginx on Centos7

Recommend

jQuery plugin to achieve seamless carousel

Seamless carousel is a very common effect, and it...

Error mysql Table 'performance_schema...Solution

The test environment is set up with a mariadb 5.7...

JavaScript to show and hide the drop-down menu

This article shares the specific code for JavaScr...

centos7.2 offline installation mysql5.7.18.tar.gz

Because of network isolation, MySQL cannot be ins...

Docker and portainer configuration methods under Linux

1. Install and use Docer CE This article takes Ce...

Mobile front-end adaptation solution (summary)

I searched online and found that many interviews ...

display:grid in CSS3, an introduction to grid layout

1. Grid layout (grid): It divides the web page in...

Linux disk sequential writing and random writing methods

1. Introduction ● Random writing will cause the h...

CocosCreator ScrollView optimization series: frame loading

Table of contents 1. Introduction 2. Analysis of ...

Create a code example of zabbix monitoring system based on Dockerfile

Use the for loop to import the zabbix image into ...

Detailed explanation of the TARGET attribute of the HTML hyperlink tag A

The hyperlink <a> tag represents a link poin...

Analysis on the problem of data loss caused by forced refresh of vuex

vuex-persistedstate Core principle: store all vue...