How do you change the position of a Component item in React?
Unless I've misunderstood it, React orders list items by key
, which is represented in the DOM by data-reactid
, but I don't know how to modify the key
of components on the page.
i.e. How do you grab the component, change it's key
, and then fire a render so that the reordered list renders in the order you've set?
e.g. in the following code example, when the Click me
link is clicked, the first list item would be swapped with the last list item.
Ideally, this functionality would allow you to dynamically reorder/relocate any component on the page without changing the order of components in the render
method.
Here is a link to the repo where the full project is located: https://github.com/bengrunfeld/gae-react-flux-todos
var TodoBox = React.createClass({
render: function(){
return (
<div className="todo-container">
<h4>GAE React Flux Todos</h4>
<TodoList data={this.state.data} />
</div>
)
}
});
var TodoList = React.createClass({
changePosition: function(e){
// Change position of list item (e.g. to top/certain position/end of list)
},
render:function(){
var todoNodes = this.props.data.map(function(todo) {
return (
<Todo key={todo.id} id={todo.id}>
{todo.todoText}
</Todo>
);
});
return (
<form className="todoList">
{todoNodes}
<a onClick={this.changePosition}>Click me</a>
</form>
)
}
});
var Todo = React.createClass({
render:function(){
return (
<div className="todoItem">
<input type="text" className={this.props.id} onChange={this.checkInput} defaultValue={this.props.children} ref="todoItem"/>
</div>
)
}
});
Approach: We are going to use following steps: Assume the position of our element is 0 on x-coordinate and 0 on y-coordinate. Then we will add/subtract some value from x/y coordinate depending on the direction we are moving.
You can set the position of any DOM-Element with position: absolute; , top : yCoordinate and left : xCoordinate css attributes. You can then set those in any shape or form and the desired result should be visible.
A component cannot update its own props unless they are arrays or objects (having a component update its own props even if possible is an anti-pattern), but can update its state and the props of its children.
The key
prop is not used to order the element, but to reconciliate it between different render
calls. Elements with the same key
will not be re-rendered but rather diffed against each other in order to update the DOM optimally. See Reconciliation
If you want to reorder elements, you need to change their position in your JSX or in the element array you pass as children in your render
method (todoNodes
).
In your case, you could make a copy of this.props.data
in the TodoList
component state, then update that copy in your changePosition
method with something like this.setState({data: reorderedData})
. A good place to make that copy would be in getInitialState
.
The render
method of your TodoList
would then be called again, and you would map over your newly reordered this.state.data
to create an array of Todo
elements ordered to your liking.
However, be aware that props in getInitialState
is an anti-pattern. Since your data lives in the state of your TodoBox
component, a way to avoid this would be to have your TodoList
component call this.props.onReorder(reorderedData)
in its changePosition
method. Your TodoBox
component could then pass an event handler to its TodoList
child, and update its state with the new data whenever this handler is called.
var TodoBox = React.createClass({
handleReorder: function(reorderedData) {
this.setState({data: reorderedData});
},
render: function(){
return (
<div className="todo-container">
<h4>GAE React Flux Todos</h4>
<TodoList data={this.state.data} onReorder={this.handleReorder} />
</div>
)
}
});
var TodoList = React.createClass({
changePosition: function(e){
// Change position of list item (e.g. to top/certain position/end of list)
// Create a copy of this.props.data and reorder it, then call
// this.props.onReorder to signal to the parent component that
// the data has been reordered
this.props.onReorder(reorderedData);
},
render:function() {
var todoNodes = this.props.data.map(function(todo) {
return (
<Todo key={todo.id} id={todo.id}>
{todo.todoText}
</Todo>
);
});
return (
<form className="todoList">
{todoNodes}
<a onClick={this.changePosition}>Click me</a>
</form>
)
}
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With