Reputation: 97
I haven't been able to understand why my App react component is rendering twice, as seen in the gif below.
I inserted a console.log just before returning the component to see how many times my component was rendering.
Whenever I remove the useState hook, my app renders just once as I suppose should be. Any guidance on why this is happening is welcome
import React, { useState, useEffect } from 'react';
const ListItem = ({ title, url, author, num_comments, points }) => {
return (
<div>
<span>
<a href={url} target='_blank' rel='noopener noreferrer'>
{title}
</a>{' '}
by {author}
</span>
<br />
<span>Comments: {num_comments}</span>
<br />
<span>Points: {points}</span>
<hr />
</div>
);
};
const List = ({ list }) => {
return list.map(({ objectID, ...item }) => (
<ListItem key={objectID} {...item} />
));
};
const Search = ({ search, onSearch }) => {
return (
<div>
<label htmlFor='search'>Search: </label>
<input id='search' type='text' value={search} onChange={onSearch} />
<p>
Searching for <strong>{search}</strong>
</p>
</div>
);
};
const App = () => {
const stories = [
{
title: 'React',
url: 'https://reactjs.org/',
author: 'Jordan Walke',
num_comments: 3,
points: 4,
objectID: 0,
},
{
title: 'Redux',
url: 'https://redux.js.org/',
author: 'Dan Abramov, Andrew Clark',
num_comments: 2,
points: 5,
objectID: 1,
},
];
const [search, setSearch] = useState(localStorage.getItem('search') || '');
useEffect(() => {
localStorage.setItem('search', search);
}, [search]);
const handleSearch = (event) => {
setSearch(event.target.value);
};
console.log('rendered');
return (
<div className='App'>
<h1>My Hacker Stories</h1>
<Search search={search} onSearch={handleSearch} />
<hr />
<List
list={stories.filter((story) =>
story.title.toLowerCase().includes(search.toLowerCase())
)}
/>
</div>
);
};
export default App;
Upvotes: 2
Views: 3029
Reputation: 1059
Even I was wondering why my controls are rendering twice.
I had no useEffect hook in most of my components and mostly I am just setting the state(immutably) as the user is entering data in input boxes. This is also happening in every component that has two why binding.
DevTools: I tried my app in a browser with no devtools installed and still the same issue.
React.StrictMode I think this is the potential culprit. When I removed this tag from index.js all the components start working correctly. I also read in the official documentation that the strictMode checks are executed only in dev mode and are ignored in production build.
This makes me think that our code is correct and we can ignore the re-rendering issue in dev.
Upvotes: 5
Reputation: 11342
Your ‘setSearch’ is updating the vue for the input box, and then your ‘useEffect’ updates it again when search changes.
Remove the useEffect
Then
const handleSearch = (event) => {
setSearch(event.target.value);
localStorage.setItem('search', event.target.value)
}
Here is a sandbox link: https://codesandbox.io/s/dawn-night-h2xiz?file=/src/App.js
It indeed doesn't fix it, but will probably avoid you some issues in the future.
The double render should only happen in development mode and not production. See Dan Abramov reponse here: https://github.com/facebook/react/issues/15074
Upvotes: 2
Reputation: 12222
Check this out : https://github.com/facebook/react-devtools/issues/1297
The "unexpected re-render" isn't actually caused by useEffect specifically– but rather, it's the way DevTools "inspects" hooks values by re-rendering the function component in isolation.
While I understand that unexpected renders can indicate problems in some cases, this particular one shouldn't actually be a problem for several reasons:
The renders aren't recursive. (Child components aren't rendered.) The renders only happen for users with DevTools installed, and even then– only impact a single component (the one currently selected in the tree). The renders don't have side effects (e.g. the DOM won't be updated).
Upvotes: 4
Reputation: 416
I agree with @denislexic, here's one way to fix the issue.
Instead of
useEffect(() => {
localStorage.setItem('search', search);
}, [search]);
const handleSearch = (event) => {
setSearch(event.target.value);
};
Let's do the following:
const handleSearch = (event) => {
const search = event.target.value;
setSearch(search);
localStorage.setItem('search', search);
};
This accomplishes the same 2 tasks (save to state, and to localStorage) in one routine instead of 2. Hint: useEffect causes a re-render
Hope that helps.
Cheers! 🍻
Upvotes: 0