Ben
Ben

Reputation: 5414

React.js: How do you change the position of a Component item

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

Answers (2)

user217782
user217782

Reputation:

Keys are used for something else, not for sorting. React uses keys 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

Alexandre Kirszenberg
Alexandre Kirszenberg

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

Related Questions