Passing an empty array as the second argument to useEffect makes it only run on mount and unmount, thus stopping any infinite loops.
useEffect(() => {
setIngredients({});
}, []);
This was clarified to me in the blog post on React hooks at https://www.robinwieruch.de/react-hooks/
Answer from WhiteFluffy on Stack OverflowPassing an empty array as the second argument to useEffect makes it only run on mount and unmount, thus stopping any infinite loops.
useEffect(() => {
setIngredients({});
}, []);
This was clarified to me in the blog post on React hooks at https://www.robinwieruch.de/react-hooks/
Had the same problem. I don't know why they not mention this in docs. Just want to add a little to Tobias Haugen answer.
To run in every component/parent rerender you need to use:
useEffect(() => {
// don't know where it can be used :/
})
To run anything only one time after component mount(will be rendered once) you need to use:
useEffect(() => {
// do anything only one time if you pass empty array []
// keep in mind, that component will be rendered one time (with default values) before we get here
}, [] )
To run anything one time on component mount and on data/data2 change:
const [data, setData] = useState(false)
const [data2, setData2] = useState('default value for first render')
useEffect(() => {
// if you pass some variable, than component will rerender after component mount one time and second time if this(in my case data or data2) is changed
// if your data is object and you want to trigger this when property of object changed, clone object like this let clone = JSON.parse(JSON.stringify(data)), change it clone.prop = 2 and setData(clone).
// if you do like this 'data.prop=2' without cloning useEffect will not be triggered, because link to data object in momory doesn't changed, even if object changed (as i understand this)
}, [data, data2] )
How i use it most of the time:
export default function Book({id}) {
const [book, bookSet] = useState(false)
const loadBookFromServer = useCallback(async () => {
let response = await fetch('api/book/' + id)
response = await response.json()
bookSet(response)
}, [id]) // every time id changed, new book will be loaded
useEffect(() => {
loadBookFromServer()
}, [loadBookFromServer]) // useEffect will run once and when id changes
if (!book) return false //first render, when useEffect did't triggered yet we will return false
return <div>{JSON.stringify(book)}</div>
}
Hey!! First time building a website and have ran into this issue when trying to fetch content from Sanity Studio.
Here's the link: https://pastebin.com/3iL0gpBt
Copying what I think is the relevant part of the code
export default function IssuesList() {
const [items, setItems] = useState([]);
useEffect(() => {
sanityClient
.fetch(
`*[_type == "issue"] | order(publishedAt desc) {
title,
slug,
description,
frontCover{
asset->{
_id,
url
}
}
}`
)
.then((data) => {
setItems(data);
})
.catch(console.error);
});
return (
<div css={issuesListSx}>
<Frame path={[{ name: "Issues", slug: "/issues" }]}>
<Grid gap={2} columns={[1, null, 2]} className="issuesGrid">
{items.map((issue) => {
return (
<div className="issueItem" key={issue.title}>
<Link to={"/issues/" + issue.slug.current}>
<div>{issue.title}</div>
{issue.frontCover && "asset" in issue.frontCover && (
<img src={issue.frontCover.asset.url} alt="" />
)}
</Link>
</div>
);
})}
</Grid>
</Frame>
</div>
);
}E: Fixed!! Had to add an empty array as a second parameter to useEffect
Bug: Infinite loop with useState and useEffect hooks
React useEffect infinite loop despite empty array
node.js - Is there any way to stop the useEffect infinite loop even after provided with second empty array argument? - Stack Overflow
reactjs - useEffect dependency array causing infinite loop - Stack Overflow
Videos
First things first,
I would like to be able to create several of SomeComponent from within App in the future.
This (at least the way you're doing it) is not something that is possible or should be done at all when using React. You cannot create a component inside another component.
The reason your useEffect is in an infinite loop can be all sorts of things at the moment. It can be that it is not positioned in the highest scope, but my guess is that the following happens:
genId() is called, and state is updated, re-render is initialized (because of state update), and const SomeComponent = () => {...} is initialized again, thus activating useEffect again.
To fix this, first things first remove <SomeComponent /> from being created inside <App /> They should be completely separated. Secondly, pass the genId function as a prop, and add it to useEffect dependency list, since you need it there.
This would be a great start since now the code is semantically and by the rules correct.
const SomeComponent = ({ genId }) => {
const [ componentId, setComponentId ] = React.useState(null);
React.useEffect(() => {
let generatedId = genId();
setComponentId(generatedId);
console.log(`generated '${generatedId}'`)
}, [genId]);
return <div>nothing works</div>
}
const App = () => {
const [ idCounter, setIdCounter ] = React.useState(0);
const genId = () => {
setIdCounter( id => id + 1 );
return `ID-${idCounter}`;
}
return <SomeComponent genId={genId} />
};
I wont answer this but just make the point having sub-components have some disadvantages. You sub-componeentn is almost imposible to unit test. Maybe you can move it to top level componennt and accept generatedId as a prop
const SomeComponent = ({generatedId}) => {
const [ componentId, setComponentId ] = React.useState(null);
React.useEffect(() => {
setComponentId( id => generatedId );
console.log(`generated '${generatedId}'`)
}, []);
return <div>nothing works</div>
}
const App = () => {
const [ idCounter, setIdCounter ] = React.useState(0);
const genId = () => {
setIdCounter( id => id + 1 );
return `ID-${idCounter}`;
}
return <SomeComponent generatedId={genId()}/>
};