0

I have to use a react component that I cannot modify. It's from an external source, due to changes. This could also be a component from a npm package that I import. This is what it looks like, a simple button:

class Button extends React.Component {

 // ... more code above  
 render() {
  const { onClick, disabled, children} = this.props;

  return (
    <button className={this.getClasses()} onClick={onClick} disabled={disabled}>
      {this.props.symbol && <Icon symbol={this.props.symbol} />}
      {children}
    </button>
  );

 }

}

How can I add some functionality with no access to the file (I can create my own component that extends the button)? For example, I want a type prop in there. I thought I can just create a <ButtonExtend onClick={resetState} type="button />.

How can I do this? Ideally I would like to make this even more flexible, so I can also do: <ButtonExtend onClick={resetState} type="submit" name="extended button" />.

I would expect the html to render all the properties from <Button> with my additional html attributes. So I want to use the functionality of the original and my additional props. Or it this not even possible, to change the render method of another component, if the component doesn't make it possible?

phifa
  • 856
  • 7
  • 11
  • Just extend your own class/component from `Button` and override its `render` method – hindmost Oct 31 '18 at 15:47
  • you can't inject attributes into the button html element even decorating the component. You could subclass it and override the render method though, if still some reusable code remains in the original component. – abidibo Oct 31 '18 at 15:47
  • @hindmost Is this not possible in a compositional kind of way? – phifa Oct 31 '18 at 15:50
  • @abidibo do you have an example? Not sure if I can follow – phifa Oct 31 '18 at 15:51
  • Sure it's possible in the same way as `Button` is extended from `React.Component` – hindmost Oct 31 '18 at 15:54
  • @hindmost If possible, could you please provide some code example? – phifa Oct 31 '18 at 15:57

2 Answers2

0

Unless a component was designed for customization, there is no straightforward way to do this.

Button is an example of badly designed component because it doesn't accept additional props. An issue and PR could be submitted to the repository in order to address original problem.

In extended component, this can be fixed by passing props from extended component.

Parent render result could be modified:

class ButtonExtend extends Button {
 // ... more code above  
 render() {
  const button = super.render();
  const { symbol, children, ...props } = this.props;

  return React.cloneElement(button, {
    children: [
      symbol && <Icon symbol={symbol} />,
      ...children
    ],
    ...props
  });
}

If an element that needs to be modified is nested, this may become messy and result in unnecessarily created elements.

A cleaner way is to paste render in extended component and modify it:

class ButtonExtend extends Button {
 // ... more code above  
 render() {
  const { symbol, children, ...props } = this.props;

  return (
    <button className={this.getClasses()} {...props}/>
      {symbol && <Icon symbol={symbol} />}
      {children}
    </button>
  )
 }
}

This way it can be used as

<ButtonExtend onClick={resetState} type="submit" name="extended button" />
Estus Flask
  • 206,104
  • 70
  • 425
  • 565
  • if component is external (from an NPM package, IE) and a particular prop is not expected, cloning an element and merge props could be useless. – Mosè Raguzzini Oct 31 '18 at 16:02
  • What do you mean by 'not expected'? onClick, etc are expected. This will result in re-rendered button with desired props. Try it yourself. – Estus Flask Oct 31 '18 at 16:14
  • Him is trying to get a different render, so events and standard props may be not enough – Mosè Raguzzini Oct 31 '18 at 16:27
  • I downvoted because React Devs promote composition over inheritance and your solution involves inheritance even if React has a pattern to manage cases as this. "At Facebook, we use React in thousands of components, and we haven’t found any use cases where we would recommend creating component inheritance hierarchies." ref: https://reactjs.org/docs/composition-vs-inheritance.html – Mosè Raguzzini Oct 31 '18 at 17:31
  • @MosèRaguzzini I'm very aware of composition over inheritance. React promotes composition where it's justified, not as a dogma that should be blindly followed. Overriding `render` result is edge case that cannot be efficiently solved with composition alone. You obviously didn't try your own answer. Try to do this with HOC as you suggest and see what happens. You will end up extending a class any way and calling component's `render` method from HOC any way like shown [here](https://stackoverflow.com/questions/52705522) in order to augment it (see `super.render()` part in question code). – Estus Flask Oct 31 '18 at 18:58
  • @estus hi thanks for the wisdom, you said I should submit a PR. I will do this no matter which approach I take now, thanks! How should that person design its component, so that I can add some more props? – phifa Oct 31 '18 at 20:06
  • @phifa ` – Estus Flask Oct 31 '18 at 20:14
  • I think this is a really bad implementation. It's from storybook, these are components that I have to use. I added `{...this.props}`to the ` – phifa Nov 01 '18 at 08:12
  • @phifa In case don't you need to pass all props, you need to filter out those ones that don't need to be passed. This is done with destructuring. I updated the answer to show how `children` is handled in this case. Hope this helps. – Estus Flask Nov 01 '18 at 08:31
0

Although public methods and properties of a component are accessible by refs (https://reactjs.org/docs/refs-and-the-dom.html) the pattern are you looking for is High Order Components (HOC, https://reactjs.org/docs/higher-order-components.html)

Mosè Raguzzini
  • 15,399
  • 1
  • 31
  • 43