Reputation: 168
What is difference between these two way of passing component as props.
RepoMenu
as it is:<Fetch
url={`https://api.github.com/users/${login}/repos`}
renderSuccess={RepoMenu}
/>
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}><</button>
<p>{name}</p>
<button onClick={next}>></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
Upvotes: 0
Views: 70
Reputation: 6046
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;
Upvotes: 1
Reputation: 15540
return renderSuccess({ data });
Your problem is you're calling a component like a function. Every time you call renderSuccess
(which is RepoMenu
- now it's not considered a component), it will trigger your custom hook useIterator
which is invalid.
You can refer to this document
Don’t call Hooks inside loops, conditions, or nested functions.
Nested functions part is your current situation.
With the pure component, it was working because you didn't use any hooks, that's why you don't see any errors come out.
For the fix, you can do it this way
import React from "react";
import { useFetch } from "./hooks/useFetch";
function Fetch({
url,
loadingFallback = <div>Loading....</div>,
errorFallback = <div>Some thing went wrong</div>,
renderSuccess: RenderSuccess //introduce it as a component
}) {
const { data, error, loading } = useFetch(url);
if (loading) {
return loadingFallback;
}
if (error) {
return errorFallback;
}
if (data) {
return <RenderSuccess data={data} />; //render a component instead of executing a function
}
return null;
}
export default Fetch;
Upvotes: 2