I’m trying to make a dynamic React loader similar to this StackOverflow post. I am having a couple of issues, but the one I would like help with is the indexing. I have a string of component names and when the element renders it loops through all of the componentNames in the list and renders them in that order with an index. Another component is used a simple component wrapper that provides buttons for moving the component up or down.
When you click the down button on component 3 it swaps with component 4 but the indexing doesn’t change.
Any pointers are much appreciated.
Here is my simplified code:
import React from 'react';
function H1(props){return <h1>H1-{props.index}</h1>}
function H2(props){return <h2>H2-{props.index}</h2>}
function H3(props){return <h3>H3-{props.index}</h3>}
export default class DynamicPageTest extends React.Component {
constructor(props) {
super(props);
this.componentMapping = {
H1:H1,
H2:H2,
H3:H3,
};
this.state = {
componentNames: ["H1","H2","H3","H2","H3"]
};
}
moveComponentUp(index){
this.swapComponents(index,index-1)
}
moveComponentDown(index){
this.swapComponents(index,index+1)
}
swapComponents(indexA,indexB){
let newComponentNames = this.state.componentNames.slice();
let temp = this.state.componentNames[indexB];
newComponentNames[indexB] = newComponentNames[indexA];
newComponentNames[indexA] = temp;
this.setState({componentNames: newComponentNames});
alert(newComponentNames)
}
render() {
let pageComponents = []
this.state.componentNames.forEach((componentName, index) => {
let callbacks = {
moveComponentUp: ()=>{this.moveComponentUp(index)},
moveComponentDown: ()=>{this.moveComponentDown(index)}
}
const Component = this.componentMapping[componentName];
let newComponent = <ComponentWrapper key ={`${componentName}`} index = {index} componentCount = {this.state.componentNames.length} callbacks = {callbacks}>
<Component index = {index}/>
</ComponentWrapper>
pageComponents.push(newComponent)
});
return (
<div >
{pageComponents}
</div>
);
}
}
class ComponentWrapper extends React.Component {
constructor(props) {
super(props);
this.children = props.children;
this.state = {
index:props.index,
callbacks: props.callbacks,
componentCount:props.componentCount
};
}
render() {
return (
<div style = {{display: "flex", flexDirection: "column", border:"1px solid black",margin:"10px 0px"}}>
<div style={{display: "flex", flexDirection: "row", position:"relative"}}>
{this.state.index}{this.children}
<div style={{display: "flex", flexDirection: "column",position: "absolute", right: "0"}}>
<div style={{height:"100%",display:"flex",flexDirection:"column",justifyContent:"baseline"}}>
{this.state.index != 0 && <button onClick = {this.state.callbacks.moveComponentUp}>Move Up</button>}
{this.state.index != this.state.componentCount - 1 && <button onClick = {this.state.callbacks.moveComponentDown}>Move Down</button>}
</div>
</div>
</div>
</div>
)
};
}
>Solution :
This is because you are setting this.state.index from the props.index in the constructor. This is not a mistake per se. But, when the component is rerendered, the state.index is set once and not updated anymore.
If you want it to be updated, use the index from the props directly.
...
{this.props.index}{this.children}
<div style={{display: "flex", flexDirection: "column",position: "absolute", right: "0"}}>
<div style={{height:"100%",display:"flex",flexDirection:"column",justifyContent:"baseline"}}>
{this.props.index != 0 && <button onClick = {this.state.callbacks.moveComponentUp}>Move Up</button>}
{this.props.index != this.state.componentCount - 1 && <button onClick = {this.state.callbacks.moveComponentDown}>Move Down</button>}
</div>
</div>
...
