【问题标题】:In what condition it will re-render while using react custom hooks在什么情况下它会在使用 react 自定义钩子时重新渲染
【发布时间】:2020-07-01 07:17:03
【问题描述】:

我在使用 react 钩子时尝试了一个示例,将其设为自定义钩子。 问题是简单的钩子useCount() 运行良好,但是打算切换高亮线的钩子useCarHighlight() 不会导致重新渲染。 我看两者是一样的,有什么问题需要注意吗?

我在这里做了一个沙盒:https://codesandbox.io/s/typescript-j2xtf

下面的一些代码:

// index.tsx

import * as React from "react";
import * as ReactDOM from "react-dom";
import useCarHighlight, { Car } from "./useCarHighlight";
import useCount from "./useCount";

const myCars: Car[] = [
  { model: "C300", brand: "benz", price: 29000, ac: "auto ac" },
  { model: "Qin", brand: "byd", price: 9000 }
];

const App = () => {
  const { cars, setHighlight } = useCarHighlight(myCars, "Qin");
  const { count, increase, decrease } = useCount(10);
  console.log(
    `re-render at ${new Date().toLocaleTimeString()}, 
    Current highlight: ${
      cars.find(c => c.highlight)?.model
    }`
  );
  return (
    <div>
      <ul>
        {cars.map(car => {
          const { model, highlight, brand, price, ac = "no ac" } = car;
          return (
            <li
              key={model}
              style={{ color: highlight ? "red" : "grey" }}
            >{`[${brand}] ${model}: $ ${price}, ${ac}`}</li>
          );
        })}
      </ul>
      <button onClick={() => setHighlight("C300")}>highlight C300</button>
      <button onClick={() => setHighlight("Qin")}>highlight Qin</button>
      <hr />

      <h1>{`Count: ${count}`}</h1>
      <button onClick={() => increase()}>+</button>
      <button onClick={() => decrease()}>-</button>
    </div>
  );
};

ReactDOM.render(<App />, document.getElementById("root"));



// useCarHighlight.ts

import { useState } from "react";

export type Car = {
  model: string;
  brand: string;
  price: number;
  ac?: "auto ac" | "manual ac";
};

export default function(
  initialCars: Car[],
  initialSelectedModel: string
): {
  cars: Array<Car & { highlight: boolean }>;
  setHighlight: (selMod: string) => void;
} {
  const carsHighlight = initialCars.map(car => ({
    ...car,
    highlight: initialSelectedModel === car.model
  }));

  const [cars, setCars] = useState(carsHighlight);
  const setHighlight = (selMod: string) => {
    cars.forEach(car => {
      car.highlight = car.model === selMod;
    });
    setCars(cars);
  };

  return {
    cars,
    setHighlight
  };
}




// useCount.ts
import { useState } from "react";

export default function useCount(initialCount: number) {
  const [state, setState] = useState(initialCount);
  const increase = () => setState(state + 1);
  const decrease = () => setState(state - 1);
  return {
    count: state,
    increase,
    decrease
  };
}

【问题讨论】:

    标签: javascript reactjs typescript frontend react-hooks


    【解决方案1】:

    与类组件不同,钩子的变异状态不会排队重新渲染,当使用钩子时,您拥有以不可变的方式更新您的状态。

    此外,在根据前一个状态计算下一个状态时,建议使用函数更新并从函数的第一个参数中读取前一个状态。

    const setHighlight = (selMod: string) => {
      setCars(prevState =>
        prevState.map(car => ({
          ...car,
          highlight: car.model === selMod
        }))
      );
    };
    

    这里有一个关于immutable update patterns的好资源

    【讨论】:

    • 嵌套字段更改不会导致重新渲染,但对象引用更改会这样做。我得到了它。谢谢,我的英雄。
    【解决方案2】:

    不要在setHighlight 中使用forEach,而是使用map

      const setHighlight = (selMod: string) => {
        const newCars = cars.map(car => ({
          ...car,
          highlight: car.model === selMod
        }));
        setCars(newCars);
      };
    

    【讨论】:

    • 嵌套字段更改不会导致重新渲染,但对象引用更改会这样做。我明白了。
    【解决方案3】:

    使用 map 而不是 forEach,因为当您更新 car 中的 highlight 属性时,car 对象的地址不会改变。

    const setHighlight = (selMod: string) => {
    let carsTemp = cars.map(car => ({
      ...car,
      highlight : car.model === selMod
    }));
    setCars(carsTemp);};
    

    【讨论】:

      猜你喜欢
      • 2021-12-27
      • 2020-03-13
      • 2022-01-10
      • 1970-01-01
      • 2022-01-02
      • 2020-08-01
      • 2021-02-21
      • 2020-08-18
      • 1970-01-01
      相关资源
      最近更新 更多