根据 Child 中的状态对 Parent 组件的 Child 组件进行反应排序
React Sort Child Components of Parent Component based on state in Child
我正在为 运行 纸笔活动开发一个工具。在这个工具中,我希望能够在彼此之下显示任意数量的生物实体。它们应该按特定顺序显示。
该顺序应该是根据生物 "ini" 的值得出的(即具有最高值的生物显示在顶部)。该秩序将始终得到维持。因此,当值发生变化时(因为它包含在输入字段中),应该再次对生物组件进行排序。
我已经制作了必要的生物组件,并制作了一个parent组件来显示它们。但我现在不知道如何排序。我已经将一个函数从 parent 传递给 children,一旦值发生变化就会触发该函数。但我不知道如何访问 children 并根据它们的状态值对它们进行排序。我对 React 还是很陌生,如果这是一件微不足道的事情,请原谅。我只是在任何地方都找不到答案!
我试过使用 React.Children.toArray(...)
但这似乎不对。我还想尝试使用 refs 来保存 child 组件。但我并不真正了解如何使用它们,也不知道如何处理。除此之外,我一无所知。
这是我的 parent 组件:
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>
)
}
}
这是它运行时的预期结果。正如人们所看到的,具有最高价值的生物在最上面。当然,只要值发生变化,它就应该求助于自己。
一如既往,你问了之后很快就会自己找到答案:
我每次渲染生物组件时都使用唯一键解决了这个问题。由于 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 but I didn't feel fully comfortable with that yet. I am using the uuidv4 Function from this thread 解决(来自 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})
}
Creatures 对象被放入一个数组中,然后该数组根据当前的主动值进行排序,然后再应用于状态,从而触发渲染。
如您所见,只是一个带有自定义比较器的简单排序函数。没有什么花哨。正如我所说,通常 React 不会关心这个列表是否已排序,因为组件的键仍然相同。所以它不会重新渲染它们。我这样解决了那个问题:
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 属性在每个渲染中都是唯一的,因此强制 react 重新渲染整个事物并因此保持排序!
我正在为 运行 纸笔活动开发一个工具。在这个工具中,我希望能够在彼此之下显示任意数量的生物实体。它们应该按特定顺序显示。
该顺序应该是根据生物 "ini" 的值得出的(即具有最高值的生物显示在顶部)。该秩序将始终得到维持。因此,当值发生变化时(因为它包含在输入字段中),应该再次对生物组件进行排序。
我已经制作了必要的生物组件,并制作了一个parent组件来显示它们。但我现在不知道如何排序。我已经将一个函数从 parent 传递给 children,一旦值发生变化就会触发该函数。但我不知道如何访问 children 并根据它们的状态值对它们进行排序。我对 React 还是很陌生,如果这是一件微不足道的事情,请原谅。我只是在任何地方都找不到答案!
我试过使用 React.Children.toArray(...)
但这似乎不对。我还想尝试使用 refs 来保存 child 组件。但我并不真正了解如何使用它们,也不知道如何处理。除此之外,我一无所知。
这是我的 parent 组件:
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>
)
}
}
这是它运行时的预期结果。正如人们所看到的,具有最高价值的生物在最上面。当然,只要值发生变化,它就应该求助于自己。
一如既往,你问了之后很快就会自己找到答案:
我每次渲染生物组件时都使用唯一键解决了这个问题。由于 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 but I didn't feel fully comfortable with that yet. I am using the uuidv4 Function from this thread 解决(来自 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})
}
Creatures 对象被放入一个数组中,然后该数组根据当前的主动值进行排序,然后再应用于状态,从而触发渲染。
如您所见,只是一个带有自定义比较器的简单排序函数。没有什么花哨。正如我所说,通常 React 不会关心这个列表是否已排序,因为组件的键仍然相同。所以它不会重新渲染它们。我这样解决了那个问题:
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 属性在每个渲染中都是唯一的,因此强制 react 重新渲染整个事物并因此保持排序!