Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React HOC—add Event Listeners to wrapped Component

Tags:

I am trying to create an HOC which makes a regular functional component »Touchable«.

So I have that an HOC like that:

const Touchable = (Component, handler) => {
  const T = ({props, children}) => {
    const Instance = React.createElement(
      Component, 
      {
        ...props,
        onTouchStart: (e) => handler.touchStart(e),
        /*more listeners*/
      }, children);
  }

  return T;
}

Another Component like so:

const Button = ({props, children}) => <div>…</div>;

export default Touchable(Button, {touchStart: () => {}});

Using this like so:

<Button>Hallo</Button>

results in (react developer Panel):

<Button onTouchStart={…}>
  <div>…</div>
</Button>

But what I really would like to have is:

<Button>
  <div onTouchStart={…}>…</div>
</Button>

I have tried to clone the Instance, but somehow I have no luck. How can I do that?

like image 982
philipp Avatar asked Nov 21 '17 09:11

philipp


2 Answers

Can't you just return the Component without React.createElement part?

Something like:

const TouchableHOC = (Component, handler) =>
  (props) =>
    <Component
      {...props}
      onClick={(event) => handler.touchStart(event)}
    />

const YourComponent = ({ children, onClick }) =>
  <button onClick={onClick}>{children}</button>


const EnhancedComponent = TouchableHOC(
  YourComponent,
  { touchStart: () => console.log('touchStart') }
)

const WithoutRest = TouchableHOC(
  'button',
  { touchStart: () => console.log('touchStart') }
)

const App = () =>
  <div>
    <EnhancedComponent>
      Click-me
    </EnhancedComponent>
    <WithoutRest>
      No props at all
    </WithoutRest>
  </div>

ReactDOM.render(
  <App />,
  document.getElementById('root')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

<div id="root"></div>
like image 146
mersocarlin Avatar answered Oct 09 '22 09:10

mersocarlin


You could make use of React.cloneElement to clone the children and attach custom props to them

const Touchable = (Component, handler) => {
  const T = ({props, children}) => {
    const Instance = React.createElement(
      Component, 
      props,
      React.cloneElement(children, {
           onTouchStart: (e) => handler.touchStart(e),
           /*more listeners*/
      });
    )
  }

  return T;
}
like image 43
Shubham Khatri Avatar answered Oct 09 '22 09:10

Shubham Khatri