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