В каком состоянии он будет перерисовываться при использовании реагирующих пользовательских хуков - PullRequest
1 голос
/ 20 марта 2020

Я попробовал пример с использованием реагирующего крючка, сделайте его пользовательским. Проблема в том, что простой хук 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
  };
}

Ответы [ 3 ]

1 голос
/ 20 марта 2020

Не используйте forEach в setHighlight, используйте map вместо

  const setHighlight = (selMod: string) => {
    const newCars = cars.map(car => ({
      ...car,
      highlight: car.model === selMod
    }));
    setCars(newCars);
  };
1 голос
/ 20 марта 2020

В отличие от компонентов класса, изменяющее состояние хуков не ставит в очередь повторный рендеринг, при использовании хуков у вас есть для неизменного обновления вашего состояния.

Также при вычислении следующее состояние, основанное на предыдущем состоянии, рекомендуется использовать функциональное обновление и прочитать предыдущее состояние из первого аргумента функции.

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

Вот хороший ресурс о неизменяемых шаблонах обновления

0 голосов
/ 20 марта 2020

Используйте карту вместо forEach, поскольку адрес автомобильного объекта не изменяется при обновлении свойства подсветки в автомобиле.

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