Reputation: 5414
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>
)
}
});
Upvotes: 5
Views: 15302
Reputation:
Keys are used for something else, not for sorting. React uses key
s to optimize its internal Virtual DOM operations. It means you tell React that "no matter the order of these siblings, the individual sibling is still identified by this key". That's how React knows whether it should prepend, insert, delete, or append new siblings by reusing the old, without throwing stuff away unnecessarily.
As for your sorting question: To change the order of the siblings, just sort the JavaScript array this.props.data
.
Upvotes: 0
Reputation: 36408
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>
)
}
});
Upvotes: 8