React Hooks - 防止子组件渲染

React Hooks - Preventing child components from rendering

作为React的新手,组件的重新渲染似乎是一件不能做的事情。 因此,例如,如果我想创建一个遵循这种架构的菜单: AppMenu 的父级,它有一个 map 函数创建 MenuItem 个组件

目前还好,只是所有的组件都重新渲染了(见各种console.log

代码如下:

应用程序

import React, { useState} from "react"
import Menu from "./menu";

function App() {

  const data = ["MenuItem1", "MenuItem2", "MenuItem3", "MenuItem4", "MenuItem5", "MenuItem6"]

  const [selectedItem, setMenuItem] = useState(null)

  const handleMenuItem = (menuItem) => {
    setMenuItem(menuItem)
  }

  return (
    <div className="App">
      <Menu items = {data} handleMenuItem = {handleMenuItem}></Menu>
      <div>{selectedItem}</div>
    </div>
  );
}

export default App;

菜单

import React from "react";
import MenuItem from "./menuItem";

const Menu = (props) => {

    return (
        <>
            {props.items.map((item, index) => {
                return <MenuItem key = {index} handleMenuItem = {props.handleMenuItem} value = {item}></MenuItem>
            })
            }
            {console.log("menuItem")}
        </>
    )
};

export default React.memo(Menu);

菜单项

import React from "react";

const MenuItem = (props) => {

    return (
        <>
        <div onClick={() => props.handleMenuItem(props.value)}>
            <p>{props.value}</p>
        </div>
        {console.log("render du MenuItem")}
        </>
    )

};

export default React.memo(MenuItem);

如您所见,我在 MenuItem 的末尾使用了 React.memo 但它不起作用,PureComponent

如果有人有想法,能得到一些建议就太好了。

祝你有美好的一天

这里有很多东西要拆开,让我们开始吧。

钩子旨在防止 re-rendering 组件不必要地使用的方法是确保您使用任何未更改变量的相同实例,尤其是对象、函数和数组。我这么说是因为字符串、数字和布尔值相等很简单 'abc' === 'abc' 解析为 true,但 [] === [] 将是 false,因为它们是两个不同的空数组进行比较,对象和函数在 JS 中相等只有当被比较的两侧是完全相同的项目时,数组才 returns 为真。

就是说,React 提供了缓存值的方法,并且仅在需要更新时(因为它们的依赖项发生变化)才更新它们(通过创建新实例)。让我们从您的 app.js

开始
import React, {useState, useCallback} from "react"
import Menu from "./menu";

// move this out of the function so that a new copy isn't created every time
// the App component re-renders

const data = ["MenuItem1", "MenuItem2", "MenuItem3", "MenuItem4", "MenuItem5", "MenuItem6"]

function App() {

  const [selectedItem, setMenuItem] = useState(null);

  // cache this with useCallback.  The second parameter (the dependency
  // array) is an empty array because there are no items that, should they
  // change, we should create a new copy.  That is to say we should never
  // need to make a new copy because we have no dependencies that could
  // change.  This will now be the same instance of the same function each 
  // re-render.
  const handleMenuItem = useCallback((menuItem) => setMenuItem(menuItem), []);

  return (
    <div className="App">
      <Menu items={data} handleMenuItem={handleMenuItem}></Menu>
      <div>{selectedItem}</div>
    </div>
  );
}

export default App;

以前,每次 App 组件 re-rendered 时,handleMenuItem 都被设置为该函数的一个新副本,并且 data 也被设置为一个新数组(具有相同的条目) re-render。这将导致每次 App 为 re-rendered 时子组件 (Menu) re-render。我们不想要那样。如果绝对必要,我们只希望子组件 re-render。

接下来是菜单组件。这里几乎没有变化,尽管我会敦促你不要在你的 JSX 中的 = 周围放置空格(key={index} 而不是 key = {index}.

import React from "react";
import MenuItem from "./menuItem";

const Menu = (props) => {

    return (
        <>
            {props.items.map((item, index) => {
                return <MenuItem key={index} handleMenuItem={props.handleMenuItem} value={item}/>
            })
            }
            {console.log("menuItem")}
        </>
    )
};

export default React.memo(Menu);

对于 MenuItem,让我们缓存点击处理程序。

import React from "react";

const MenuItem = (props) => {
  // cache this function
  const handleClick = useCallback(() => props.handleMenuItem(props.value), [props.value]);

    return (
        <>
        <div onClick={handleClick}>
            <p>{props.value}</p>
        </div>
        {console.log("render du MenuItem")}
        </>
    )

};

export default React.memo(MenuItem);

useCallback 包装您的 handleMenuItem 函数,以避免在函数更改时重新呈现。这将创建一个函数引用,它将在 MenuItem 中用作 props 并且将避免重读,因为它始终是相同的函数实例。

在这种情况下,我使用了一个空的依赖项数组,这对您的用例来说是正确的。如果您的函数有任何状态引用,则应将它们添加到数组中。

  const handleMenuItem = useCallback((menuItem) => {
    setMenuItem(menuItem);
  }, []);