【问题标题】:How to avoid unnecessary rendering in component with hooks如何避免在带有钩子的组件中进行不必要的渲染
【发布时间】:2020-06-01 21:52:54
【问题描述】:

您好,我有一个主题提供者,它有两个主题和情感提供者主题

我有一个组件 Header,我通过 useTheme 获得这个主题:

const Header = () => {
  const Theme = useTheme();
  const [text, setText] = useState('');
  console.log(Theme, 'this is header');
  const handleClick = () => {
    setText('');
  };
  return (
    <Container theme={Theme}>
      <Content theme={Theme} flexdirection={'column'}>
        <textarea
          onKeyPress={e => {
            if (e.key === 'Enter') {
              e.preventDefault();
            }
            if (e.key === 'Enter') {
              handleClick();
            }
          }}
          value={text}
          onChange={e => setText(e.target.value)}
          placeholder="Digite aqui sua mensagem"
        />
        <Top theme={Theme} />
        <NavBar theme={Theme} />
      </Content>
    </Container>
  );
};

然后我将我的主题作为孩子的道具传递:

const NavBar = props => {
  console.log(props.theme, 'this is nav bar');
  return (
    <Nav>
      <ul>
        <li>
          <a href="#home">home</a>
        </li>
        <li>
          <a href="#homex">test</a>
        </li>
        <li>
          <a href="#homexv">test</a>
        </li>
      </ul>
    </Nav>
  );
};

但是在这个组件中,我有一个文本字段,我会在输入时渲染,但我的主题是一起渲染的,就像在 gif 中一样:

这是我控制主题的钩子:

export default function useAppTheme(defaultTheme = lightTheme) {
  const [theme, _setTheme] = useState(getInitialTheme);

  function getInitialTheme() {
    const savedTheme = localStorage.getItem('theme');
    if (savedTheme === 'dark' || savedTheme === 'light') {
      return JSON.parse(savedTheme) === 'dark' ? darkTheme : defaultTheme;
    } else {
      return defaultTheme;
    }
  }

  useEffect(() => {
    localStorage.setItem('theme', JSON.stringify(theme.type));
  }, [theme]);

  return {
    theme,
    setTheme: ({ setTheme, ...theme }) => {
      if (theme.type === 'dark') {
        return _setTheme(darkTheme);
      } else {
        return _setTheme(lightTheme);
      }
    },
  };
}

和我的主要组成部分:

export default function App() {
  const { theme, setTheme } = useAppTheme();

  useEffect(() => {
    console.log('rendering app');
  });

  return (
    <ThemeProvider theme={theme}>
      <GlobalStyle />
      <div className="App">
        <Header />
        <button
          css={css`
            background: red;
            width: 100px;
            height: 50px;
            border-radius: 10px;
          `}
          onClick={() =>
            setTheme(
              theme.type === 'dark' ? { type: 'light' } : { type: 'dark' },
            )
          }
        >
          a
        </button>
      </div>
    </ThemeProvider>

【问题讨论】:

    标签: reactjs


    【解决方案1】:

    据我了解,您的代码按预期工作。我想我可以解释如下。

    Header 由于状态更新(调用setText onChange)而重新渲染。如果一个组件被重新渲染,每个子组件都需要重新渲染(即使道具没有改变)。您可以使用shouldComponentUpdate 或作为PureComponent 或memoized function(al) 组件退出此重新渲染。在这里,我们可以看到NavBar 只是一个状态功能组件,它会在每次状态更新时重新渲染。详情请见here。

    您可以使用React.memo 解决此问题,如下所示:-

    const NavBar = React.memo(props => {
      console.log(props.theme, 'this is nav bar');
      return (
        <Nav>
          <ul>
            <li>
              <a href="#home">home</a>
            </li>
            <li>
              <a href="#homex">test</a>
            </li>
            <li>
              <a href="#homexv">test</a>
            </li>
          </ul>
        </Nav>
      );
    });
    

    现在你的组件不会被重新渲染,除非 props 真的改变了。

    【讨论】:

      猜你喜欢
      • 2020-07-26
      • 2021-12-25
      • 2021-01-14
      • 2020-05-04
      • 1970-01-01
      • 2018-03-17
      • 2020-07-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多