How to access props in a functional HOC?

Viewed 11562

I have code similar to the following:

const HOC = (props, WrappedComponent) => {
    return (
       <>
          <WrappedComponent />
          <Icon className="props.iconStyles" />
       </>
     );
};

This is not actually valid code, but you can hopefully see what I'm trying to accomplish. I want to be able to use it in the following way:

<HOC iconStyles="icon-stuff">
    <TheComponentIWantToWrap>
</HOC>

How can I write this correctly, so as to be able to pass props through? I think I might need to be using children here too, not sure.

4 Answers

It would be something like this.

const HOC = (WrappedComponent) => {

  const MyComponent = props => {
    return (
       <>
          <WrappedComponent {...props} />
          <Icon className={props.iconStyles} />
       </>
     );
   }
   
  return MyComponent;
};

An HOC is a funtion which returns a Component and usually inject some props on it. You should separate concerns. An actual HOC should look like this

const withFoo = Component =>{
    return props =>{
        return <Component injectedProps='foo' />
    }
}

And be called like this

const Component = withFoo(({ injectedProps }) =>{
    return <jsx />
})

If you want to merge arbitrary props as well try to spread the props passed to Component

const withFoo = Wrapped =>{
    return props =>{
        return <Wrapped injectedProps='foo' {...props}/>
    }
}

<Component propsToBeSpreaded='bar' />

If you prefer you can create an aditional layer.

  • HOC injects some props in a Container
  • Container accepts arbitrary props
  • Container renders children

The code

   const withHoc = Container =>{
        return () => <Container injected='foo' />
    }

    const Container = withHoc(({ children, injected, ...aditionalProps}) =>{
         return(
             <div {...aditionalProps}>
                 { children }
             </div>
         )
    })

And call it like

const FinalComp = () =>{
    return <Container foo='foo'> <span /> </Container>
}

A higher-order component would return another component (another function in this case). A component is a function that returns JSX.

const HOC = (Component) => {
  return function WrappedComponent(props) {
    return (
       <>
          <Component {...props} />
          <Icon className="props.iconStyles" />
       </>
     );
  };
};

You could still pass down props along with a Component to be enhanced (as per your original approach which you think is wrong) It is right since -

HOCs are not part of React API. They emerge from React's nature of composition.

So your HOC usage is -

const EnhancedComponent = higherOrderComponent(WrappedComponent, anyProps);

Points to note:

  1. Your HOC takes in a Component and returns a Component (enhanced or not)

    const higherOrderComponent = (WrappedComponent, anyProps) => {
        return class extends React.Component {
            // Component body
        }
    } 

  1. Don’t Mutate the Original Component. Use Composition.
  2. Pass Unrelated Props Through to the Wrapped Component.
    const higherOrderComponent = (WrappedComponent, anyProps) => {
        return class extends React.Component {
            render() {
                const { HOCProp, ...compProps } = this.props;
                return (
                    <WrappedComponent
                        ...compProps,
                        someProp={anyProps.someProp}
                    />
                );
            }
        }
    }

Considering all this, your HOC would look like this -

    const withWrappedIcon = (CompToWrap, iconStyle) => {
        return (
           <Icon className={iconStyle}>
              <CompToWrap />
           </Icon>
        );
    }
Related