为TypeScrip类提供对Reaction挂钩状态的访问

x4shl7ld  于 2022-10-15  发布在  React
关注(0)|答案(1)|浏览(122)

我正在尝试使用Reaction来显示UI元素,并使用TypeScrip类来表示游戏的状态,来制作一款游戏。
以下是我用来表示数据的几个类的示例:

export class Place extends Entity {
  items: Item[];
  npcs: NPC[];
  location: LatLng | null;
  onEnter: (...args: any[]) => any = () => {};
  constructor(
    name: string,
    description: string,
    location?: LatLng,
    onEnter: (...args: any[]) => any = () => {},
    items: Item[] = [],
    npcs: NPC[] = []
  ) {
    super(name, description);
    this.items = items;
    this.npcs = npcs;
    this.location = location ? location : null;
    this.onEnter = onEnter;
  }

export class Item extends Entity {
  url: string;
  constructor(
    name: string,
    description: string,
    actions = {},
    url = "https://upload.wikimedia.org/wikipedia/commons/thumb/4/46/Question_mark_%28black%29.svg/1920px-Question_mark_%28black%29.svg.png"
  ) {
    super(name, description);
    this.url = url;
    this.actions = actions;
  }
}

export class NPC {
  name: string;
  description: string;
  messages: Message[];
  url: string;
  timesTalkedTo = 0;
  constructor(
    name: string,
    description: string,
    url = "https://cdn.icon-icons.com/icons2/1378/PNG/512/avatardefault_92824.png",
    messages: Message[] = []
  ) {
    this.name = name;
    this.description = description;
    this.messages = messages;
    this.url = url;
  }

  getMsg() {
    console.log(this.messages);
    if (this.messages.length > 1) {
      for (var i = 1; i < this.messages.length; i++) {
        const msg = this.messages[i];
        if (msg["cond"] && msg["cond"]()) {
          this.timesTalkedTo += 1;
          return msg;
        }
      }
    }
    this.timesTalkedTo += 1;
    return this.messages[0];
  }
}

稍后,我将这些类的示例存储在钩子中,以便可以使用我定义的其他组件显示它们:

function UI() {
  const [places, setPlaces] = useState({});
  const [inventory, setInventory] = useState([]);
  const [playerPlace, setPlayerPlace] = useState(outside);
  const [playerLocation, setPlayerLocation] = useState(L.latLng([0, 0]));
  ...

我的问题是,我想在我的UI组件中定义一个类和类似这样的函数,这样我就能够访问setState挂钩,并对我定义为Droppable的任何项使用“Drop”和“Pick”操作:

class Droppable extends Item {
    dropped;
    constructor(
      name,
      description,
      actions = {},
      dropped = true,
      url = "https://upload.wikimedia.org/wikipedia/commons/thumb/4/46/Question_mark_%28black%29.svg/1920px-Question_mark_%28black%29.svg.png"
    ) {
      super(name, description, actions, url);
      this.dropped = dropped;
      const drop = () => {
        addToPlace(removeFromInventory(this));
        this.dropped = true;
        this.actions["pick up"] = pickUp;
        delete this.actions["drop"];
      };
      const pickUp = () => {
        addToInventory(removeFromPlace(this));
        this.dropped = false;
        this.actions["drop"] = drop;
        delete this.actions["pick up"];
      };
      if (dropped) {
        this.actions["pick up"] = pickUp;
      } else {
        this.actions["drop"] = drop;
      }
    }
  }

  const addToInventory = useCallback(
    (item) => {
      setInventory((inv) => [...inv, item]);
      return item;
    },
    [setInventory]
  );
  const removeFromInventory = useCallback(
    (item) => {
      setInventory((inv) => inv.filter((i) => i !== item));
      return item;
    },
    [setInventory]
  );

  const addToPlace = useCallback(
    (item) => {
      setPlaces((places) => {
        return {
          ...places,
          [playerPlace.name]: {
            ...playerPlace,
            items: [...playerPlace.items, item],
          },
        };
      });
      return item;
    },
    [setPlaces, playerPlace]
  );

  const removeFromPlace = useCallback(
    (item) => {
      setPlaces((places) => {
        const newPlace = { ...places[playerPlace.name] };
        newPlace.items = newPlace.items.filter((i) => i !== item);
        const newPlaces = [...places];
        newPlaces[playerPlace.name] = newPlace;
        return newPlaces;
      });
      return item;
    },
    [setPlaces, playerPlace]
  );

然而,当我尝试从它所在的位置移除物品并将其添加到玩家的库存中(“Pick”操作)时,我发现,虽然它已成功添加到库存中,但无法从该位置移除,因为playerPlace状态变量已过时。即使成功调用了setPlayerPlace并将playerPlace设置为包含项的位置,但该值仍被设置为其初始的空位置,因此在尝试访问该位置的项时会出错。
我的猜测是,这些回调没有根据状态正确更新,因为它们是在我定义的类中使用的,但我想不出任何其他方法来使类中的方法能够轻松访问状态变量。
以这种方式在React的同时使用普通类是不是一个坏主意?如果是这样的话,有什么更好的方式来构建我的应用程序呢?如果没有,我如何才能让我的类访问Reaction组件中的状态呢?

06odsfpq

06odsfpq1#

如果您真的想使用类,我建议将类移出组件,然后将setter和数据作为参数传递给类。您也可以为此使用第三方状态管理库,然后将其连接在一起,但我认为这并不真正值得。一般来说,在Reaction中为状态使用类是一种反模式IMHO。通常,如果我需要封装功能,我只会编写类型,然后为这些类型编写实用函数。除了使用Reaction之外,这还有许多好处,比如可以轻松地将数据序列化为JSON(它们现在是POJO)。

相关问题