'React: Ways of passing component as a props

What is difference between these two way of passing component as props.

  1. Passing RepoMenu as it is:
<Fetch
  url={`https://api.github.com/users/${login}/repos`}
  renderSuccess={RepoMenu}
/>
  1. Wrapping the RepoMenu in a function
<Fetch
  url={`https://api.github.com/users/${login}/repos`}
  renderSuccess={({ data }) => <RepoMenu data={data} />}
/>

RepoMenu internally uses a custom hook.

function RepoMenu({ data, onSelect = (f) => f }) {
  const [{ name }, prev, next] = useIterator(data);

  return (
    <div style={{ display: "flex" }}>
      <button onClick={prev}>&lt;</button>
      <p>{name}</p>
      <button onClick={next}>&gt;</button>
    </div>
  );
}

Using one (Passing RepoMenu as it is) starts failing, React starts throwing an exception Rendered more hooks than during the previous render.

Initially i thought may it is not possible to use the component like the method 1. But to test it more further i created a small Pure Component and just tried to render the data passed to it.

function RepoMenuPure({ data = [], onSelect = (f) => f }) {
  return (
    <div style={{ display: "flex" }}>
      <h3>There are {data.length} repos for this user</h3>
    </div>
  );
}

And when i tried to use it like below, It worked in this case.

const UserRepositories = ({ login, selectedRepo, onSelect = (f) => f }) => {
  return (
    <Fetch
      url={`https://api.github.com/users/${login}/repos`}
      renderSuccess={RepoMenuPure}
    />
  );
};

Can someone please help me explain what is happening and what i am missing?

I have created a small sandbox here: https://codesandbox.io/s/stackoverflow-example-bqfn3e?file=/src/App.js



Solution 1:[1]

In the first option :

renderSuccess={({ data }) => <RepoMenu data={data} />}

You are saying that Fetch component expects that renderSuccess receives a function that returns a Component. If you were using typescript, the type of renderSuccess in Fetch would be something like:

renderSuccess : (data: any) => JSX.Element

// OR return the component itself
renderSuccess : (data: any) => RepoMenu

And that´s why when you call renderSuccess({ data }) inside Fetch component works.

In the second option:

renderSuccess={RepoMenu}

You are saying that Fetch component expects that renderSuccess receives the RepoMenu Component (not a function). If you were using typescript, the type of renderSuccess in Fetch would be something like:

renderSuccess : JSX.Element

// OR the component itself
renderSuccess : RepoMenu

For this option work, you should change your Fetch component to something like this:

import React from "react";
import { useFetch } from "./hooks/useFetch";

function Fetch({
  url,
  loadingFallback = <div>Loading....</div>,
  errorFallback = <div>Some thing went wrong</div>,
  renderSuccess: Component
  //renderSuccess
}) {
  // console.log("renderSuccess", renderSuccess);

  const { data, error, loading } = useFetch(url);

  if (loading) {
    return loadingFallback;
  }

  if (error) {
    return errorFallback;
  }

  if (data) {
    // return renderSuccess({ data });
    return <Component data={data} />;
  }

  return null;
}

export default Fetch;

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Luis Paulo Pinto