2

I haven't been able to understand why my App react component is rendering twice, as seen in the gif below.

enter image description here

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;
1

4 Answers 4

5

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.

Sign up to request clarification or add additional context in comments.

Comments

4

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).

2 Comments

If you remove the strict mode it won't re render either, with or without dev tools.
Yeah, that worked. Removing the <React.StrictMode> Comp fixed the double rendering.
2

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

1 Comment

If I remove the useEffect entirely, the component keeps rendering twice.
0

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! 🍻

2 Comments

Yeah, I tried that but didn't work. Even deleting the useEffect hook entirely from the application keeps rendering the component twice (or at least logging it twice).
@FelipeRueda Here's a modification of your example code. Note: the use of useEffect is only to show you when the component renders. CodeSandbox It use memoization to avoid re-renders.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.