React.useCallback :它不会阻止连续重新渲染

React.useCallback : it doesn't prevent continuos re-rendering

我正在尝试在 React.useEffect 中使用 React.useCallback 函数以避免连续重新渲染。

我的 objective 是,一旦选择了排序选项,即使添加了新元素,也会保持列表排序。但是没有连续渲染。 这就是为什么我尝试使用 React.useCallback

但是到目前为止我所做的并没有阻止连续重新渲染...

这些 React.useCallback 函数有什么问题?

 const memoizedIncrByName = React.useCallback(() => {
    let new_data = [...data].sort((a, b) => {
      if (b.name < a.name) return 1;
      if (b.name > a.name) return -1;
      return 0;
    });
    setData(new_data);
  }, [data]);

  const memoizedIncrByEmail = React.useCallback(() => {
    let new_data = [...data].sort((a, b) => {
      if (b.email < a.email) return 1;
      if (b.email > a.email) return -1;
      return 0;
    });
    setData(new_data);
  }, [data]);

  React.useEffect(() => {
    console.log("SelectedOption:", selectedSortingOption);
    if (selectedSortingOption !== null) {
      if (selectedSortingOption.value === "name") {
        memoizedIncrByName();
      } else if (selectedSortingOption.value === "email") {
        memoizedIncrByEmail();
      }
    }
  }, [memoizedIncrByName, memoizedIncrByEmail, selectedSortingOption]);



    <Select
          defaultValue={selectedSortingOption}
          onChange={SetSelectedSortingOption}
          options={sortingOptions}
        />

data 样本:

let new_users = [
  {
    id: 5,
    name: "Chelsey Dietrich",
    username: "Kamren",
    email: "Lucio_Hettinger@annie.ca",
    address: {
      street: "Skiles Walks",
      suite: "Suite 351",
      city: "Roscoeview",
      zipcode: "33263",
      geo: {
        lat: "-31.8129",
        lng: "62.5342"
      }
    },
    phone: "(254)954-1289",
    website: "demarco.info",
    company: {
      name: "Keebler LLC",
      catchPhrase: "User-centric fault-tolerant solution",
      bs: "revolutionize end-to-end systems"
    }
  },
  {
    id: 6,
    name: "Mrs. Dennis Schulist",
    username: "Leopoldo_Corkery",
    email: "Karley_Dach@jasper.info",
    address: {
      street: "Norberto Crossing",
      suite: "Apt. 950",
      city: "South Christy",
      zipcode: "23505-1337",
      geo: {
        lat: "-71.4197",
        lng: "71.7478"
      }
    },
    phone: "1-477-935-8478 x6430",
    website: "ola.org",
    company: {
      name: "Considine-Lockman",
      catchPhrase: "Synchronised bottom-line interface",
      bs: "e-enable innovative applications"
    }
  },

更新 1)

根据Nicholas Tower的明智建议,我尝试了这种方式:

  const sortedData = React.useMemo(() => {
    if (selectedSortingOption !== null) {
      if (selectedSortingOption.value === "name") {
         return [...data].sort((a, b) => {
          if (b.name < a.name) return 1;
          if (b.name > a.name) return -1;
          return 0;
        });
      } else if (selectedSortingOption.value === "email") {
        return [...data].sort((a, b) => {
          if (b.email < a.email) return 1;
          if (b.email > a.email) return -1;
          return 0;
        })
      } else {
        return data;
      }
    } else {
      return data;
    }
  }, [data, selectedSortingOption]);

现在它不再连续重新渲染了,但是,令人惊讶的是,当我在数据中添加一个新项目时,更新 data,它不会重新渲染。为了使新数据出现,我必须更改 selectedSortingOption。 所以,我想,还有其他问题需要解决

这是点击按钮时调用的函数 AddEmployee :

const [newEmployee, setNewEmployee] = React.useState([]);

  const addSingleEmployee = () => {
    if (new_users.length === 0) {
      return;
    }
    let employee = new_users[0];

    let employeeArray = [];
    employeeArray.push(employee);
    setNewEmployee(employeeArray);

    new_users.shift();
    newData = data;
    newData.push(employee);
    setData(newData);
  };
 const memoizedIncrByName = React.useCallback(() => {
    let new_data = [...data].sort((a, b) => {
      if (b.name < a.name) return 1;
      if (b.name > a.name) return -1;
      return 0;
    });
    setData(new_data);
  }, [data]);

每次数据更改时,记忆都会中断。但是每次调用该函数时,都会更改数据,从而破坏了记忆。由于每次 memoizedIncrByName 或 memoizedIncrByEmail 更改时您的 useEffect 运行s,您都会进入渲染循环,创建一个新的 memoizedIncrByName,运行 效果,调用 memoizedIncrByName,它会再次渲染并重复该过程。

如果你想保留 useEffect,我建议将函数移到效果内部,使效果仅取决于排序选项:

React.useEffect(() => {
  const incrByName = () => {
    let new_data = [...data].sort((a, b) => {
      if (b.name < a.name) return 1;
      if (b.name > a.name) return -1;
      return 0;
    });
    setData(new_data);
  };
  const incrByEmail = () => {
    let new_data = [...data].sort((a, b) => {
      if (b.email < a.email) return 1;
      if (b.email > a.email) return -1;
      return 0;
    });
    setData(new_data);
  }

  if (selectedSortingOption !== null) {
      if (selectedSortingOption.value === "name") {
        incrByName();
      } else if (selectedSortingOption.value === "email") {
        incrByEmail();
      }
    }
}, [selectedSortingOption]);

但我实际上建议完全放弃 useEffect。当您使用 use Effect 方法时,每次您想要更改排序时,您最终都必须渲染两次:一次更新排序状态,然后当 useEffect 更新数据状态时再次渲染。相反,您可以让排序列表成为在渲染期间计算的值。为了提高性能,您可以将计算包装在 useMemo 中以在没有任何更改的情况下跳过计算:

const [data, setData] = useState(/* the full unsorted array */);
const [selectedSortingOption, setSelectedSortingOption] = useState(null);

const sortedData = useMemo(() => {
  if (selectedSortingOption.value === "name") {
     return [...data].sort((a, b) => {
      if (b.name < a.name) return 1;
      if (b.name > a.name) return -1;
      return 0;
    });
  } else if (selectedSortingOption.value === "email") {
    return [...data].sort((a, b) => {
      if (b.email < a.email) return 1;
      if (b.email > a.email) return -1;
      return 0;
    })
  } else {
    return data;
  } 
}, [data, selectedSortingOption]);

// Use sortedData below here

编辑:对于 addSingleEmployee 函数,您正在改变状态。确保制作一个新阵列。所以而不是 this:1

newData = data;
newData.push(employee);
setData(newData);

这样做:

const newData = [...data];
newData.push(employee);
setData(newData);