Explain three ways of sharing component logic in React in detail

  • 2021-10-27 06:13:00
  • OfStack

Cut the crap, these three ways are: render props, high-level components and custom Hook. The following is a demonstration in turn

Suppose there is an TimeOnPage component dedicated to recording the user's stay time on the current page, like this:


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

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

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

1, I naturally think of the way of nesting 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 within the TimeOnPage component, and has not yet achieved the goal of encapsulation and reuse. See how render props does it.

render props

"render prop" refers to a simple technique for prop sharing code with a value as a function between React components

Following the above, define an prop whose value is a function in TimeOnPage. If you want to render any component, you can return it in the function. The parameters of the function are state that 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 that tells the component what to render.
React Router also uses this technology.


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

High-order component

High-order component (HOC) is an advanced technique for reusing component logic in React. HOC itself is not a part of React API, but a design pattern based on the combined characteristics of React.

The higher-order component is a function, the parameter is a component A to be reused, and the return value is a new component N. The new component N is based on the component A, but it will not modify the component A itself, but only enhance its functions.

Suppose you have a news listing component that looks like this:


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

You want to display the loading animation component during news listings loading < Loading / > That's what you usually do


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

Suppose the Table component now displays the loading animation component during loading of data, following a similar pattern


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

Above, you will find that DataList and NewList are very similar in structure. If there are still third and fourth components to add loading, will you continue to repeat this pattern for the third and fourth times? This is not ideal. It is better to use high-level components to abstract this pattern:


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

You can then add loading to them without modifying NewList and DataList, respectively


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} />;
 }
};
//  Belt loading Adj. NewList
const WithLoadingNewList = WithLoading(<NewList />)
//  Belt loading Adj. DataList
const WithLoadingDataList = WithLoading(<DataList />)

Custom Hook

Hook is a new feature of React 16.8. It allows you to use state and other React features without writing class.

React Hook has useState, useEffect and so on, which are all functions. Custom Hook is also a function, its name also begins with use, and other Hook can be called inside the function. Unlike the React component, a custom Hook can have no return value. Unlike ordinary functions, custom Hook can call other Hook internally, while ordinary functions cannot.

In the process of writing business logic, 1 will generally define a number of reusable methods as tool functions, which can then be reused everywhere. Similarly, by customizing Hook, component logic can be extracted into reusable functions. Whether to choose custom Hook or tool function depends on whether other Hook is needed for the component logic to be extracted. If so, choose custom Hook, otherwise use tool function.

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


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

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

Usage


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>
 );
}

0

Summarize

The three ways to share component logic have their own applicable scenarios:
render props is suitable for sharing parent components with different child components/child elements. The "pit position" of child components/child elements has been defined and can only be rendered at specified positions;
High-order components are suitable for extending components without modifying original components.
Custom Hook can do, pure functions can basically do, but sometimes with custom Hook implementation will be more convenient and fast.
Link to this article: Github


Related articles: