📜 ⬆️ ⬇️

Higher order components using Recompose

HOC is too loud a word for a simple functional pattern!

A month ago in RaiffeisenBank, the first front-end mitap took place, and since I just prepared a presentation on “High order components with functional patterns using Recompose” in just a couple of days, I didn’t have time to prepare any information about Recompose on the Internet. reference material, and did not even write their contact details at the end of the presentation, which was not very good. And to the question: “Where can we see your slides?”, I hesitated and did not answer, for which I apologize very much.

I want to correct the situation and write reference material, as well as release a series of articles in which I will tell in detail all that my presentation was devoted to.

The Recompose library has very poor documentation, which is not understandable to everyone because it does not contain explanatory examples. I will try to close this gap, and at the end of the cycle we will even touch RxJS.
')
In this article, I will talk about how to compose and decompose components and start with simple short questions and definitions, and show with examples what the stateless component looks like and then the stateful component.

And the first question is “What is the name of a component that does not have a state?”



The stateless component is a component that does not have a state.

Example (arrow function):

const stateComponent = ({name}) => <div>{name}</div> 



The second question is “What is the name of a component that has a state?


A stateful componen t is a component that has a state.

Now imagine that you do not need to use the state and use the life cycle methods in your stateful component. More precisely, you can use it all, but carrying it out and then re-using it in different components. And this is done with the help of HOC.

What is HOC?

High Order Component is a function that accepts a component and returns a new enhanced component.

Abstractly it looks like this:



Here you can see that the function accepts the arg1 and arg2 arguments and returns a function that accepts the Component component and returns a new enhanced EnhancedComponent component.

Hoc can be of two types:

1. stateless



2. stateful



The stateful component-a has the advantage that we can specify not only the template but also the methods of the life cycle.

Usage example:



Here, using the HOC component is created Bob. In the first part of the HOC-a, we pass the object {name: “Bob”} as an argument, and in the second part, the component on the basis of which we obtain the “improved” component of Bob.

→ A live example of using a higher order component by reference



Recompose


Recompose is a library with ready-made components of higher order. The idea is to write stateless components and divide the code into logical parts. Using ready-made HOCs, you can separate the methods of the life cycle, make business logic and hang event handlers not inside the component, but outside. At the same time, reuse everything that you used and create your own components based on the base ones.

Recompose created by Andrew Clarke, more information can be found in the official repository: github.com/acdlite/recompose.

And now let's take a closer look at the word Recompose and remove the first two letters. We obtain the compose method, which is very often used to apply several HOCs.

Let's see what compose is.



Suppose we have a function that takes an argument:



And what if we wanted to put one function in another:



And then invest the result of the execution also in the third function:



Imagine that you have twenty functions that you want to execute sequentially for one argument. What a nightmare will be. This is where the compose method comes in.



Compose accepts a list of functions in the first part, and a second argument for which functions will be executed. And the order of performance of functions begins from the end:

  1. func1
  2. func2
  3. func3

Now, remember that hoc is a function that accepts a component and returns a new component. And since this is a function, we can, with the help of compose, apply several hocs for one component. And consider a simple example of how compose interacts with the setDisplayName and setPropTypes methods from recompose :



setDisplayName - takes a string and sets the displayName (display name) for the component.
setPropTypes - accepts an object with props that can be reused in other HOCs or in the arguments themselves.

→ Live example by reference

 const { Component, PropTypes } = React; const { compose, setDisplayName, setPropTypes } = Recompose; const enhance = compose( setDisplayName('User'), setPropTypes({ name: React.PropTypes.string.isRequired, status: React.PropTypes.string }) ); const User = enhance(({ name, status, dispatch }) => <div className="User" onClick={ () => dispatch({ type: "USER_SELECTED" }) }> { name }: { status } </div> ); console.log(User.displayName); ReactDOM.render( <User name="Tim" status="active" />, document.getElementById('main') ); 

Now step by step:

1. We import the setDisplayName and setPropTypes methods from the Recompose library, but due to codepen.io limitations, destructuring is used here instead of import. In the enhance variable, I write the higher order components setDisplayName and setPropTypes .

 const { Component, PropTypes } = React; const { compose, setDisplayName, setPropTypes } = Recompose; const { connect } = Redux(); const enhance = compose( setDisplayName('User'), setPropTypes({ name: React.PropTypes.string.isRequired, status: React.PropTypes.string }), ); 

2. Then use the enhance method for a stateless component.

 const User = enhance(({ name, status, dispatch }) => <div className="User"> { name }: { status } </div> ); 

3. Render

 ReactDOM.render( <User name="Tim" status="active" />, document.getElementById('main') ); 

Please note that here in the compose method we specified only the first part, which consists of three HOCs, and wrote it into the enhance variable, and did not specify the second part at all.

To understand why we did it, you need to understand how the compose method works and
understand that this is a higher order function:

A higher order function is a function that accepts other functions and returns a new function.

Now let's briefly describe the compose method:

 function compose(...funcs) { return funcs.reduce((a, b) => (...args) => a(b(...args))) } 

  1. A pure function sheet is passed to the arguments of the compose method;
  2. Next, this list of functions is iterated using the reduce method;
  3. In the reduce method, a and b are passed as arguments, where a is the battery function and b is the function currently being executed.
  4. The body of a function of a reduce method is nothing more than a recursive function that will iterate through an array of functions from the end.

withState & withHandlers


withState is a hoc that takes three arguments:

1. stateName - the name of the state that can be accessed;
2. stateUpdaterName - the name of the pure function that will update the state;
3. initialState - the initial state (initial state);

Consider an example.



Suppose we have two components Status and Tooltip, it is clear that these two components have a state and some event handlers that change the same state, but only under different circumstances. The StatusList will appear in the Status component when you click on the component, and the text appears in the Tooltip component when you hover the cursor over the block.



State of these components is exactly the same and has the same initial state.



What do event handlers do? Each method handles the same flag in its own way, but even with such differences they can be combined in one HOC-e.

Now imagine that we can pull out the state and event handlers from the component. How? The answer is simple: “recompose HOCs”!



The only difference between these components is the render method. But it can be taken out into a stateless component, which we will do.

Now back to the subtitle withState & withHandlers and first withState, and after withHandlers.

withState

Create a simple component, when hovering over which the Tooltip will appear, and when you click on the status, the StatusList will be displayed.

→ Live example by reference

 const { Component } = React; const { compose, withState } = Recompose; //  compose  withState const StatusList = () => // StatusList -         Active <div className="StatusList"> <div>pending</div> <div>inactive</div> <div>active</div> </div>; //  hoc withState, //    isToggle —  ,  toggle -   stateUpdater- //    initialState const Status = withState('isToggle', 'toggle', false) (({ status, isToggle, toggle }) => // 'isToggle', 'toggle'     <span onClick={ () => toggle(!isToggle) }> {/*  event onClick    */} { status } { isToggle && <StatusList /> } </span> ); //  hoc withState, //    isToggle —  ,  toggle -   stateUpdater- //    initialState const Tooltip = withState('isToggle', 'toggle', false) (({ text, children, isToggle, toggle }) => // 'isToggle', 'toggle'     <span> { isToggle && <div className="Tooltip">{ text }</div> } <span onMouseEnter={ () => toggle(true) } onMouseLeave={ () => toggle(false) }>{ children }</span> {/*  event- onMouseEnter  onMouseLeave    */} </span> ); //  hoc withState, //    isToggle —  ,  toggle -   stateUpdater- //    initialState const User = ({ name, status }) => <div className="User"> <Tooltip text="Cool Dude!">{ name }</Tooltip>— <Status status={ status } /> </div>; const App = () => <div> <User name="Tim" status="active" /> </div>; ReactDOM.render( <App />, document.getElementById('main') ); 

You can see that withState ('isToggle', 'toggle', false) is repeated for two components, so let's move it into the withToggle variable:

→ Live example by reference

 const { Component } = React; const { compose, withState } = Recompose; //  compose  withState const StatusList = () => // StatusList -         Active <div className="StatusList"> <div>pending</div> <div>inactive</div> <div>active</div> </div>; //  hoc withState,       //    isToggle —  ,  toggle -   stateUpdater- //    initialState const withToggle = withState('isToggle', 'toggle', false); const Status = withToggle(({ status, isToggle, toggle }) => // 'isToggle', 'toggle'     <span onClick={ () => toggle(!isToggle) }> {/*  event onClick    */} { status } { isToggle && <StatusList /> } </span> ); //  hoc withState, //    isToggle —  ,  toggle -   stateUpdater- //    initialState const Tooltip = withToggle(({ text, children, isToggle, toggle }) => // 'isToggle', 'toggle'     <span> { isToggle && <div className="Tooltip">{ text }</div> } <span onMouseEnter={ () => toggle(true) } onMouseLeave={ () => toggle(false) }>{ children }</span> {/*  event- onMouseEnter, onMouseLeave    */} </span> ); //  hoc withState, //    isToggle —  ,  toggle -   stateUpdater- //    initialState const User = ({ name, status }) => <div className="User"> <Tooltip text="Cool Dude!">{ name }</Tooltip>— <Status status={ status } /> </div>; const App = () => <div> <User name="Tim" status="active" /> </div>; ReactDOM.render( <App />, document.getElementById('main') ); 

With the help of withHandlers, we can move event handlers to hoc and call the component from the props. Consider how

 const { Component } = React; const { compose, withState, withHandlers } = Recompose; //  compose, withState  withHandlers const withToggle = compose( //   withState & withHandlers   compose withState('toggledOn', 'toggle', false), withHandlers({ // withHandlers     //      toggle,   stateUpdater-    show: ({ toggle }) => (e) => toggle(true), hide: ({ toggle }) => (e) => toggle(false), toggle: ({ toggle }) => (e) => toggle((current) => !current) }) ) const StatusList = () => // StatusList -         Active <div className="StatusList"> <div>pending</div> <div>inactive</div> <div>active</div> </div>; const Status = withToggle(({ status, toggledOn, toggle }) => <span onClick={ toggle }> { status } { toggledOn && <StatusList /> } </span> ); const Tooltip = withToggle(({ text, children, toggledOn, show, hide }) => <span> { toggledOn && <div className="Tooltip">{ text }</div> } <span onMouseEnter={ show } onMouseLeave={ hide }>{ children }</span> </span> ); const User = ({ name, status }) => <div className="User"> <Tooltip text="Cool Dude!">{ name }</Tooltip>— <Status status={ status } /> </div>; const App = () => <div> <User name="Tim" status="active" /> </div>; ReactDOM.render( <App />, document.getElementById('main') ); 

→ Live example by reference

And now let's see how our code looked before and after:





WithReducer


 withReducer<S, A>( stateName: string, dispatchName: string, reducer: (state: S, action: A) => S, initialState: S | (ownerProps: Object) => S ): HigherOrderComponent 

The withReducer is similar to the withState method and has a similar structure, but the state is updated using the reducer function. Consider an example:

→ Live example by reference

 const { Component } = React; const { compose, withReducer, withHandlers } = Recompose; //  compose, withReducer  withHandlers const withToggle = compose( withReducer('toggledOn', 'dispatch', (state, action) => { switch(action.type) { //    case 'SHOW': return true; case 'HIDE': return false; case 'TOGGLE': return !state; default: return state; } }, false), withHandlers({ show: ({ dispatch }) => (e) => dispatch({ type: 'SHOW' }), //  action-   dispatch hide: ({ dispatch }) => (e) => dispatch({ type: 'HIDE' }), toggle: ({ dispatch }) => (e) => dispatch({ type: 'TOGGLE' }) }) ); const StatusList = () => // StatusList -         Active <div className="StatusList"> <div>pending</div> <div>inactive</div> <div>active</div> </div>; const Status = withToggle(({ status, toggledOn, toggle }) => <span onClick={ toggle }> { status } { toggledOn && <StatusList /> } </span> ); const Tooltip = withToggle(({ text, children, toggledOn, show, hide }) => <span> { toggledOn && <div className="Tooltip">{ text }</div> } <span onMouseEnter={ show } onMouseLeave={ hide }>{ children }</span> </span> ); const User = ({ name, status }) => <div className="User"> <Tooltip text="Cool Dude!">{ name }</Tooltip>— <Status status={ status } /> </div>; const App = () => <div> <User name="Tim" status="active" /> </div>; 

Conclusion:

  1. Composition and decomposition of components
  2. We can use only stateless components.
  3. Higher-order components allow you to create something similar to decorators and add impurities to a component.
  4. Small HOC utilities can be packaged into large and useful HOCs and

Source: https://habr.com/ru/post/354768/


All Articles