React Component design pattern – Composite components

React component design mode – provider-consumer

When writing business, we often need to abstract logic that is used frequently, but RenderProps is a good way to abstract logic except for higher-order components.

RenderProps, as the name implies, is to render the props of a component. You actually let the component’s props receive functions that render the content. The reuse logic is achieved by abstracting the common logic inside the component and then calling functions (functions rendering content within the props) based on the business logic.

Simple implementation

Let’s start with the simplest implementation of RenderProps:

const RenderProps = props => <>
    {props.children(props)}
</>
Copy the code

In this case, the child of the RenderProps component is a function props. Children (props), and props. Children returns the UI element.

The code is as follows:

<RenderProps>
    {() => <>Hello RenderProps</>}
</RenderProps>
Copy the code

What is the use of this without any business logic? We can imagine manipulating the returned results with code in the RenderProps component. Take the most common user login logic as an example, expect to see the content only after login, otherwise display please login:

const Auth = props => {
    const userName = getUserName()
    if(userName) { const allProps = {userName, ... props}return <>
            {props.children(allProps)}
        </>
    } else {
        return<> Please login </>}} <Auth> {({userName}) => <>Hello! {userName}</>} </Auth>Copy the code

Props. Children (allProps) is equivalent to Auth component nested ({userName}) => <>Hello! {userName}

In the example above, getUserName returns the user name if the user is logged in, otherwise it returns null. So we can determine what to return. Of course, userName is passed in through renderProps, which is an enhancement of the Auth component.

The function name can be more than children

When used normally, props. Children are concrete component instances, but this implementation is based on functions as children(props), which are called back to the UI. Again, any function in props can be called. Take the logic above as an example:

const Auth = props => {
  const userName = 'Mike'
  if(userName) { const allProps = { userName, ... props }return <>{props.login(allProps)}</>
  } else {
    return <>
      {props.noLogin(props)}
    </>
  }
}

Copy the code

The usage method is as follows:

<Auth
    login={({userName}) => <h1>Hello {userName}</h1>}
    noLogin={() => <h1>please login</h1>}
  />
Copy the code

Here, the Auth component props receives two functions: login(indicating that the table is logged in) and noLogin(indicating that the table is not logged in). Inside the Auth component, it determines which component to display by judging whether it is logged in.

conclusion

As a way of abstracting general logic, render-props itself suffers from the same nesting problem as higher-order components.

<GrandFather> {Props => { <Father> {props => { <Son {... props} />; }} </Father>; }} </GrandFather>Copy the code

However, unlike higher-order components, because they render functions (which render components), they present an opportunity to take advantage of Compose. Such as the react – powerplugin.

import { compose } from 'react-powerplug'const ComposeComponent = compose( <GrandFather />, <Father /> ) <ComposeComponent> {props => { <Son {... props} />; }} <ComposeComponent/>Copy the code

Epitath also offers a new model to address this problem. This part is a whole other topic, and I’m still trying to figure it out.