0
votes

Please see this codesandbox. I am trying to create a react HOC that receives both a functional component and a function that gets invoked in the HOC.

To do this, I need to create a react component that returns this HOC. The function that needs to be invoked in the HOC needs to share it's scope with the react component, since it might do things like change the component's state or make api calls. My attempt at this is is trying to create a nested react component, but when I do, I get the following error:

Warning: Functions are not valid as a React child. This may happen if you return a Component instead of from render. Or maybe you meant to call this function rather than return it in SampleComponent

How can I create a react HOC that accepts both a react component and a function to be invoked? Thanks! The code can also be found below:

index.tsx:

import * as React from "react";
import { render } from "react-dom";
import SampleComponent from "./sampleComponent";

render(<SampleComponent />, document.getElementById("root"));

sampleWrapper.tsx:

import * as React from "react";

const sampleWrapper = (
  WrappedComponent: React.FC,
  onButtonClick: () => void
): React.FC => {
  const Component: React.FC = () => (
    <div>
      <button onClick={onButtonClick} type="button">
        Click Me
      </button>
      <WrappedComponent />
    </div>
  );

  return Component;
};

export default sampleWrapper;

sampleComponent.tsx:

import * as React from "react";
import sampleWrapper from "./sampleWrapper";

const SampleComponent: React.FC = () => {
  const [title, setTitle] = React.useState("hello world");

  const handleTitleChange = (): void => {
    setTitle("This is a new title");
  };

  const SampleInnerComponent: React.FC = () => <h1>{title}</h1>;

  return sampleWrapper(SampleInnerComponent, handleTitleChange);
};

export default SampleComponent;
2
return <Component/>;. You need to return a component instance.Brian Thompson

2 Answers

1
votes

It seems you are not returning the React Element, but the Component. You want to return the element React.ReactElemnt. This is what you want i think:

const sampleWrapper = (
  WrappedComponent: React.FC,
  onButtonClick: () => void
): React.FC => {


  return (<div>
  <button onClick={onButtonClick} type="button">
    Click Me
  </button>
  <WrappedComponent />
</div>);
};

another alternative :

import * as React from "react";

const sampleWrapper = (
  WrappedComponent: React.FC,
  onButtonClick: () => void
): React.FC => {
  const Component: React.FC = () => (
    <div>
      <button onClick={onButtonClick} type="button">
        Click Me
      </button>
      <WrappedComponent />
    </div>
  );

  return <Component/>;
};

export default sampleWrapper;
1
votes

As your sampleWrapper returns a functional component. What you need to do is save the returned functional component into a variable and render the component the same way you do functional component. i.e

import * as React from "react";
import sampleWrapper from "./sampleWrapper";

const SampleComponent: React.FC = () => {
  const [title, setTitle] = React.useState("hello world");

  const handleTitleChange = (): void => {
    setTitle("This is a new title");
  };

  const SampleInnerComponent: React.FC = () => <h1>{title}</h1>;

  const ReturnedSampleComponent = sampleWrapper(SampleInnerComponent, handleTitleChange);

  return <ReturnedSampleComponent />;
};

export default SampleComponent;

You can check this codesandbox