2

Let's say i have a tabbed component. Each tab presents a person object. The user can switch the active tab, modify or remove each one and even change their order:

state={
  activeTab:0,
  tabs:[
    {
      name:'John',
      age:34

    },
    {
      name:'Bob',
      age:31

    },
  ]
}

Let's say i want to modify one of the fields, of a specific tab(person). I could have this function:

modifyTab = (index, prop, value) => {

  const tabs = [...this.state.tabs];

  const tab = tabs[index];

  tab[prop] = value;

  this.setState({ tabs })
}

The problem with this, is that it relies on the index of the given tab. But what if the tab index changes, if i provide, let's say, the ability of switching the tab order?(like the browsers do for their tabs).

Or what if i need to register some callback for an a-sync operation, that might be called when the relevant person sits in a different tab(maybe the tab was moved from 1 to 0, by the time the callback was called)?

Is there any way to just rely on object reference, regardless of id, index or any other "identifier", which makes the code much more complicated than it needs to be?

For those who are familiar with VueJS and Angular, i'm sure you know how easy it is to modify objects, being that you do not need to return a new state tree on each change.

i.brod
  • 3,993
  • 11
  • 38
  • 74
  • What about a unique identifier for each object? Then filter out the one you need – Brian Le Mar 16 '19 at 12:12
  • That's the thing, is there a way to avoid relying on identifiers? Should i just give some arbitrary random id to each tab? Ignore the fact that the tab represents people, who might actually have id's. It might as well be something else. – i.brod Mar 16 '19 at 12:14
  • 1
    If you are changing the order of the array, you cannot rely on the `index` for the `key` prop when you are rendering. And yes, passing in the entire object reference to `modifyTab` would be perfectly fine. You could figure out what object in the array that is with a simple `indexOf`. – Tholle Mar 16 '19 at 12:14
  • 1
    Tholle, that's a very good idea, i'll try that – i.brod Mar 16 '19 at 12:15
  • Unique identifier would be useful, besides you might need to store the users in the db later on – Brian Le Mar 16 '19 at 12:15

1 Answers1

3

If you are changing the order of the array, you cannot rely on the array index for the key prop when you are rendering. One common way around this is to add a unique property to every object in the array and use that instead, e.g. an id.

Passing in the entire object reference to modifyTab would be perfectly fine. You could figure out what object in the array that is with a simple indexOf.

Example

class App extends React.Component {
  state = {
    tabs: [
      {
        name: "John",
        age: 34,
        id: 1
      },
      {
        name: "Bob",
        age: 31,
        id: 2
      }
    ]
  };

  modifyTab = (tab, prop, value) => {
    this.setState(prevState => {
      const tabs = [...prevState.tabs];
      const index = tabs.indexOf(tab);

      tabs[index] = { ...tabs[index], [prop]: value };

      return { tabs };
    });
  };

  render() {
    return (
      <div>
        {this.state.tabs.map(tab => (
          <span
            key={tab.id}
            onClick={() => this.modifyTab(tab, "name", Math.random())}
            style={{ margin: '0 10px' }}
          >
            {tab.name}
          </span>
        ))}
      </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

<div id="root"></div>
Tholle
  • 108,070
  • 19
  • 198
  • 189