简体   繁体   English

使用 useState 钩子时反应组件渲染两次

[英]React component rendering twice when using useState hook

I haven't been able to understand why my App react component is rendering twice, as seen in the gif below.我一直无法理解为什么我的 App react 组件会渲染两次,如下图所示。

在此处输入图像描述

I inserted a console.log just before returning the component to see how many times my component was rendering.我在返回组件之前插入了一个 console.log,以查看我的组件渲染了多少次。

Whenever I remove the useState hook, my app renders just once as I suppose should be.每当我删除 useState 钩子时,我的应用程序只会按我想的那样呈现一次。 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;

Check this out: https://github.com/facebook/react-devtools/issues/1297看看这个: 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. “意外的重新渲染”实际上并不是由 useEffect 引起的,而是 DevTools 通过单独重新渲染 function 组件来“检查”挂钩值的方式。

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). (不渲染子组件。)渲染只发生在安装了 DevTools 的用户身上,即使那样——也只会影响单个组件(当前在树中选择的那个)。 The renders don't have side effects (eg the DOM won't be updated).渲染没有副作用(例如 DOM 不会被更新)。

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.我的大多数组件中都没有 useEffect 挂钩,而且大多数情况下我只是在用户在输入框中输入数据时设置状态(不可变)。 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. DevTools:我在没有安装 devtools 的浏览器中尝试了我的应用程序,但仍然是同样的问题。

React.StrictMode I think this is the potential culprit. React.StrictMode我认为这是潜在的罪魁祸首。 When I removed this tag from index.js all the components start working correctly.当我从 index.js 中删除这个标签时,所有组件都开始正常工作。 I also read in the official documentation that the strictMode checks are executed only in dev mode and are ignored in production build.我还在官方文档中读到,strictMode 检查仅在开发模式下执行,在生产构建中被忽略。

This makes me think that our code is correct and we can ignore the re-rendering issue in dev.这让我觉得我们的代码是正确的,我们可以忽略 dev.js 中的重新渲染问题。

Your 'setSearch' is updating the vue for the input box, and then your 'useEffect' updates it again when search changes.您的“setSearch”正在更新输入框的 vue,然后您的“useEffect”在搜索更改时再次更新它。

Remove the useEffect删除 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这是一个沙盒链接: 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请参阅此处的 Dan Abramov回复:https://github.com/facebook/react/issues/15074

I agree with @denislexic, here's one way to fix the issue.我同意@denislexic,这是解决问题的一种方法。

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这在一个例程而不是 2 个例程中完成了相同的 2 个任务(保存到 state 和 localStorage)。提示:useEffect 导致重新渲染

Hope that helps.希望有帮助。

Cheers!干杯!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM