我正在开发一种用于运行“纸笔运动”的工具。在这个工具中,我希望能够显示任意数量的生物实体。应该以一定的顺序显示它们。
该顺序应从生物的“ ini”值得出(即,具有最高值的生物显示在顶部)。该顺序始终保持不变。因此,当值更改时(因为它包含在输入字段中),应该对生物组成部分进行重新排序。
我已经制作了必要的生物组件,并制作了父组件来显示它们。但是我现在不知道如何对其进行排序。我已经将一个函数从父级传递给子级,一旦值更改,该函数就会被触发。但是我不知道如何访问子项并根据其状态值对子项进行排序。我对React还是很陌生,所以如果这是一件小事,请原谅。我只是在任何地方都找不到答案!
我尝试使用React.Children.toArray(...)
,但这似乎不正确。我还想尝试使用refs保存子组件。但是我并不真正了解如何使用它们,也不知道该如何处理。除此之外,我还很笨。
这是我的父组件:
export class Encounter extends React.Component<IEncounterProps,IEncounterState> {
sortByIni(event) {
//somehow trigger something that sorts the creature components
}
addCreature() {
let creature =
<Creature
name={'Ancient Bugbear'}
//I removed other props for the sake of reducing verbosity...
ini={12}
/>;
return creature
}
render(): any {
return (
<div>
{this.addCreature()}
{this.addCreature()}
{this.addCreature()}
</div>
)
}
}
这是生物的组成部分:
export interface ICreatureProps {
name: string,
ini: number,
//Removing non relevant props for verbosity reducing...
}
export interface ICreatureState {
hitpoints: number
armorclass: number
ini: number
}
export class Creature extends React.Component<ICreatureProps, ICreatureState> {
constructor(props) {
super(props);
this.state= {
hitpoints: this.props.hitpoints || 0,
armorclass: this.props.armorclass || 0,
ini: this.props.ini || 0
};
this.handleIniChange = this.handleIniChange.bind(this);
this.handleACChange = this.handleACChange.bind(this);
this.handleHPChange = this.handleHPChange.bind(this)
}
handleIniChange(event) {
this.setState({ini: event.target.value})
}
handleACChange(event) {
this.setState({armorclass: event.target.value})
}
handleHPChange(event) {
this.setState({hitpoints: event.target.value})
}
render(): any {
return (
<div>
<div className={style.creatureContainer}>
<div className={style.nextToTitleContainer}>
<p className={style.statDisplay}>TP: <input type="number" className={style.inputField} value={this.state.hitpoints} onChange={this.handleHPChange}/></p>
<p className={style.statDisplay}>RK: <input type="number" className={style.inputField} value={this.state.armorclass} onChange={this.handleACChange}/></p>
//This is the field that is supposed to trigger sorting when it changes value
<p className={style.statDisplay}>INI:<input type="number" className={style.inputField} value={this.state.ini} onChange={e=>{this.handleIniChange(e); this.props.sortByIni(e)}}/></p>
</div>
</div>
</div>
)
}
}
这是预期的结果。正如人们所看到的,具有最高价值的生物在最上面。当然,只要值改变,它就应该求助于自己。
答案 0 :(得分:0)
一如既往,您问了一下,便找到了答案:
我每次渲染生物组件时都使用唯一的键来解决它。由于react通过键标识组件,因此由于ini值的更改,每次渲染时它们都必须是新的。如果没有,react不会改变dom树的顺序,因为具有该键的组件仍然存在。
我将此功能添加到Encounter
中,以基于单击按钮生成生物:
addCreature() {
let creatureMap = this.state.creatureMap;
let creatureEntry =
{
id: this.uuidv4(),
name: Math.random().toString(36).substring(7),
ini: 2,
currentIni: Math.floor(Math.random() * 20) + 2,
};
creatureMap.push(creatureEntry);
let creatureMapSorted = creatureMap.sort((creatureA, creatureB) => {
if (creatureA.currentIni < creatureB.currentIni) return 1;
if (creatureA.currentIni > creatureB.currentIni) return -1;
return 0;
});
this.setState({
creatureMap: creatureMapSorted
});
}
如您所见,我给每个生物一个唯一的ID。这很可能也可以通过Refs来解决,但是我对此还不太满意。我正在使用this thread中的uuidv4函数(来自Briguy37的回答)。当输入字段中的ini
值更改时,生物将给出其ID,以便可以在生物图谱中对其进行更改:
<p className={style.statDisplay}>INI:<input type="number" className={style.inputField} defaultValue={this.state.currentIni} onBlur={e=>{this.handleIniChange(e); this.props.sortByIni(e,this.props.id)}}/></p>
然后在Encounter
sortByIni(event,id) {
let creatureMap = this.state.creatureMap;
creatureMap.filter(creature=> {
return creature.id == id
})[0].currentIni = parseInt(event.target.value);
let creatureMapSorted = creatureMap.sort((creatureA,creatureB) => {
if (creatureA.currentIni < creatureB.currentIni) return 1;
if (creatureA.currentIni > creatureB.currentIni) return -1;
return 0;
});
this.setState({creatureMap : creatureMapSorted})
}
将生物对象放入一个数组中,然后将该数组根据当前初始值进行排序,然后将其应用于状态,从而触发渲染。
如您所见,只有带有自定义比较器的简单排序函数。没有什么花哨。现在,正如我所说,通常反应不会在意此列表是否已排序,因为组件的键仍然相同。因此它不会重新渲染它们。我这样解决了这个问题:
render(): any {
return (
<div>
<button type="button" onClick={this.addCreature}>Add Creature</button>
{this.state.creatureMap.map((creature, i) => {
return (
<Creature
id={creature.id}
name={creature.name}
hitpoints={creature.hitpoints}
key={this.uuidv4()}
*/Shortened...*/
/>
)
})}
</div>
像这样,key属性在每个渲染中都是唯一的,因此迫使做出反应以重新渲染整个事物,并因此保持其排序!