

Info , TrendChart and DailyChart .Info component. Right now it is a simple SVG icon. class Info extends React.Component { render() {   return (     <svg       className="Icon-svg Icon--hoverable-svg"       height={this.props.height}       viewBox="0 0 16 16" width="16">         <path d="M9 8a1 1 0 0 0-1-1H5.5a1 1 0 1 0 0 2H7v4a1 1 0 0 0 2 0zM4 0h8a4 4 0 0 1 4 4v8a4 4 0 0 1-4 4H4a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4zm4 5.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3z" />     </svg>   ) } } onMouseOver and onMouseOut . The function that is passed onMouseOver will be called if the mouse pointer hits the component area, and the function passed to onMouseOut will be called when the pointer leaves the component. In order to organize all this in the manner that is accepted in React, we add the hovering property to the component, which is stored in the state, which allows us to re-render the component, showing or hiding the tooltip, if this property changes. class Info extends React.Component { state = { hovering: false } mouseOver = () => this.setState({ hovering: true }) mouseOut = () => this.setState({ hovering: false }) render() {   return (     <>       {this.state.hovering === true         ? <Tooltip id={this.props.id} />         : null}       <svg         onMouseOver={this.mouseOver}         onMouseOut={this.mouseOut}         className="Icon-svg Icon--hoverable-svg"         height={this.props.height}         viewBox="0 0 16 16" width="16">           <path d="M9 8a1 1 0 0 0-1-1H5.5a1 1 0 1 0 0 2H7v4a1 1 0 0 0 2 0zM4 0h8a4 4 0 0 1 4 4v8a4 4 0 0 1-4 4H4a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4zm4 5.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3z" />       </svg>     </>   ) } } TrendChart and DailyChart . The above mechanism for the Info component works fine, it’s not necessary to repair it, so let's recreate the same thing in other components using the same code. TrendChart component TrendChart . class TrendChart extends React.Component { state = { hovering: false } mouseOver = () => this.setState({ hovering: true }) mouseOut = () => this.setState({ hovering: false }) render() {   return (     <>       {this.state.hovering === true         ? <Tooltip id={this.props.id}/>         : null}       <Chart         type='trend'         onMouseOver={this.mouseOver}         onMouseOut={this.mouseOut}       />     </>   ) } } DailyChart . class DailyChart extends React.Component { state = { hovering: false } mouseOver = () => this.setState({ hovering: true }) mouseOut = () => this.setState({ hovering: false }) render() {   return (     <>       {this.state.hovering === true         ? <Tooltip id={this.props.id}/>         : null}       <Chart         type='daily'         onMouseOver={this.mouseOver}         onMouseOut={this.mouseOut}       />     </>   ) } }  function add (x, y) { return x + y } function addFive (x, addReference) { return addReference(x, 5) } addFive(10, add) // 15 add function to the addFive function as an argument, rename it to addReference and then call it. function add (x,y) { return x + y } function higherOrderFunction (x, callback) { return callback(x, 5) } higherOrderFunction(10, add)  [1,2,3].map((i) => i + 5) _.filter([1,2,3,4], (n) => n % 2 === 0 ); $('#btn').on('click', () => console.log('Callbacks are everywhere') ) addFive function, we want to create the addTen function, and addTwenty , and other similar functions. Considering how the addFive function is addFive , we will have to copy its code and change it to create the above-mentioned functions based on it. function add (x, y) { return x + y } function addFive (x, addReference) { return addReference(x, 5) } function addTen (x, addReference) { return addReference(x, 10) } function addTwenty (x, addReference) { return addReference(x, 20) } addFive(10, add) // 15 addTen(10, add) // 20 addTwenty(10, add) // 30 addFive , addTen , addTwenty , and so on) as we need, while minimizing code duplication. Maybe to achieve this goal we need to create a function makeAdder ? This function can take a certain number and a link to the add function. Since the purpose of this function is to create a new function that adds the number passed to it to the specified one, we can make the function makeAdder return a new function, in which a certain number is specified (like the number 5 in makeFive ), and which could accept the numbers for addition with this number. function add (x, y) { return x + y } function makeAdder (x, addReference) { return function (y) {   return addReference(x, y) } } const addFive = makeAdder(5, add) const addTen = makeAdder(10, add) const addTwenty = makeAdder(20, add) addFive(10) // 15 addTen(10) // 20 addTwenty(10) // 30 add functions as needed, and at the same time minimize the amount of duplication of code..bind method used in JavaScript.makeAdder higher-order makeAdder helps us minimize code duplication, what’s called a “higher order component” will help us deal with the same problem in a React application. However, everything will look different here. Namely, instead of a work scheme in which a higher order function returns a new function that calls a callback, a higher order component can implement its own scheme. Namely, it is able to return a new component that renders the component playing the role of a callback. Perhaps we have already spoken a lot of things, so it's time to move on to examples. function higherOrderFunction (callback) { return function () {   return callback() } }  function higherOrderComponent (Component) { return class extends React.Component {   render() {     return <Component />   } } }  state = { hovering: false } mouseOver = () => this.setState({ hovering: true }) mouseOut = () => this.setState({ hovering: false }) withHover ) to encapsulate the event-handling code of the mouse, and then pass the hovering property to the components it renders. This will allow us to prevent duplication of the corresponding code by placing it in the withHover component.hovering property, we can transfer this component to a component of a higher order withHover . That is, we want to work with the components as shown below. const InfoWithHover = withHover(Info) const TrendChartWithHover = withHover(TrendChart) const DailyChartWithHover = withHover(DailyChart) withHover is rendered, it will be the source component to which the hovering property is passed. function Info ({ hovering, height }) { return (   <>     {hovering === true       ? <Tooltip id={this.props.id} />       : null}     <svg       className="Icon-svg Icon--hoverable-svg"       height={height}       viewBox="0 0 16 16" width="16">         <path d="M9 8a1 1 0 0 0-1-1H5.5a1 1 0 1 0 0 2H7v4a1 1 0 0 0 2 0zM4 0h8a4 4 0 0 1 4 4v8a4 4 0 0 1-4 4H4a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4zm4 5.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3z" />     </svg>   </> ) } withHover component. From the above it can be understood that he must perform three actions:hovering property. function withHover (Component) { }  function withHover (Component) { return class WithHover extends React.Component { } } hovering property? In fact, we have already written the code for working with this property. We just need to add it to the new component, and then pass the hovering property to it when rendering the component passed to the higher order component as the Component argument. function withHover(Component) { return class WithHover extends React.Component {   state = { hovering: false }   mouseOver = () => this.setState({ hovering: true })   mouseOut = () => this.setState({ hovering: false })   render() {     return (       <div onMouseOver={this.mouseOver} onMouseOut={this.mouseOut}>         <Component hovering={this.state.hovering} />       </div>     );   } } } Info , TrendChart and DailyChart components into new components that, thanks to the hovering property, know whether the mouse pointer is over them.withHover , you will notice that it has at least one weak point. It implies that the receiving component of the hovering property will not experience any problems with this property. In most cases, probably, this assumption is justified, but it may happen that this is unacceptable. For example, what if a component already has a hovering property? In this case, there will be a name conflict. Therefore, a withHover can be made to the withHover component, which is to allow the user of this component to indicate what name the hovering property should pass to the components. Since withHover is just a function, let's rewrite it so that it withHover second argument that specifies the name of the property passed to the component. function withHover(Component, propName = 'hovering') { return class WithHover extends React.Component {   state = { hovering: false }   mouseOver = () => this.setState({ hovering: true })   mouseOut = () => this.setState({ hovering: false })   render() {     const props = {       [propName]: this.state.hovering     }     return (       <div onMouseOver={this.mouseOver} onMouseOut={this.mouseOut}>         <Component {...props} />       </div>     );   } } } hovering , thanks to the default parameter setting mechanism of ES6, but if the user of the withHover component wants to change this, he can pass, in this second argument, the name he needs. function withHover(Component, propName = 'hovering') { return class WithHover extends React.Component {   state = { hovering: false }   mouseOver = () => this.setState({ hovering: true })   mouseOut = () => this.setState({ hovering: false })   render() {     const props = {       [propName]: this.state.hovering     }     return (       <div onMouseOver={this.mouseOver} onMouseOut={this.mouseOut}>         <Component {...props} />       </div>     );   } } } function Info ({ showTooltip, height }) { return (   <>     {showTooltip === true       ? <Tooltip id={this.props.id} />       : null}     <svg       className="Icon-svg Icon--hoverable-svg"       height={height}       viewBox="0 0 16 16" width="16">         <path d="M9 8a1 1 0 0 0-1-1H5.5a1 1 0 1 0 0 2H7v4a1 1 0 0 0 2 0zM4 0h8a4 4 0 0 1 4 4v8a4 4 0 0 1-4 4H4a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4zm4 5.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3z" />     </svg>   </> ) } const InfoWithHover = withHover(Info, 'showTooltip') withHover implementation. If we analyze our Info component, we can see that it, among other things, accepts the height property. The way everything is arranged now means that the height will be set to undefined . The reason for this is that the withHover component is the component responsible for rendering what is passed to it as the Component argument. Now we have no properties, except for the hovering we created, we do not pass the Component component. const InfoWithHover = withHover(Info) ... return <InfoWithHover height="16px" /> height property is passed to the InfoWithHover component. And what is this component? This is the component that we return from withHover . function withHover(Component, propName = 'hovering') { return class WithHover extends React.Component {   state = { hovering: false }   mouseOver = () => this.setState({ hovering: true })   mouseOut = () => this.setState({ hovering: false })   render() {     console.log(this.props) // { height: "16px" }     const props = {       [propName]: this.state.hovering     }     return (       <div onMouseOver={this.mouseOver} onMouseOut={this.mouseOut}>         <Component {...props} />       </div>     );   } } } WithHover component this.props.height is 16px , but we do not do anything with this property in the future. We need to make this property be passed to the Component argument, which we are rendering. render() {     const props = {       [propName]: this.state.hovering,       ...this.props,     }     return (       <div onMouseOver={this.mouseOver} onMouseOut={this.mouseOut}>         <Component {...props} />       </div>     ); } withRouter React Router. In accordance with the documentation, withRouter will pass the match , location and history properties to the wrapped component when it is rendered. class Game extends React.Component { render() {   const { match, location, history } = this.props // From React Router   ... } } export default withRouter(Game) Game element (i.e., <Game /> ). We completely transfer our component React Router and trust this component not only rendering, but also transferring the correct properties to our component. We have already encountered this problem above when we talked about possible name conflicts when passing hovering properties. In order to fix this, we decided to allow the HOC withHover use the second argument to set the name of the corresponding property. Using someone else's HOC withRouter we do not have this opportunity. If the match , location or history properties are already used in the Game component, then we can say that we are not lucky. Namely, we either have to change these names in our component, or refuse to use HOC withRouter .
Source: https://habr.com/ru/post/428572/
All Articles