2

I have a small implementation of drag and drop using react-dnd. There are two columns, dragging from right side column to the left one activates the specific status item.

On drop, pushCard() is called, which (as the name suggests), pushes the dragged item into the array of activated statuses i.e., status_data.

But the problem is, status_data.push(itemToPush), pushes the new item at the end of the array. I wanted to push the item on top of the array i.e., index 0 of the array.

status_data.unshift(itemToPush) works in this case, but unshift only updates the array in the state and on the backend, but it doesn't show the updated array on the front-end. Rather it would just keep pushing the same element that was dragged first.

Simple description of problem in a GIF.

pushCard:

pushCard(item) {
    const { status_data } = this.state.template;
    const itemToPush = {
        title : item.title || 'CUSTOM',
        type_id : item.type_id,
        color : item.color || '#000',
        type: item.type,
        require_notes: item.require_notes || false,
        require_estimate: item.require_estimate || false
    };
    status_data.unshift(itemToPush);
    this.setState(Object.assign(this.state.template, { status_data }));
}

renderActiveStatuses

renderActiveStatuses() {
    let renderedResult = '';
    if (this.state.template.status_data.length < 0) {
      renderedResult = (<p>No status active in this template.</p>);
    } else {
      renderedResult = this.state.template.status_data.map((status, i) => {
        return (
          <Status deleteStatus={this.deleteStatus} handleStatusUpdate={this.onStatusUpdate} index={i} id={status.id} moveCard={this.moveCard} statusData={status} key={status.id} />
        );
      });
    }
    return renderedResult;
}

renderActiveStatuses is called in the render function of the component.

2 Answers2

4

The status objects, like the itemToPush that you show here, have no property id, the one you are using as a key in Status. You can try key={i} instead (even if using the map index is not the best idea).

You can generate a (probably) unique ID like this:

const itemToPush = {
    id: Math.random().toString(36).substr(2, 16), 
    ...
}

and use status.id now as you did before.

There are better ID generators if there is any risk in the case you generate millions of these.

JulienD
  • 7,102
  • 9
  • 50
  • 84
1

What about this?

this.setState({template: Object.assign({}, this.state.template, { status_data })});

As you did in your question, you are only assigning to your state the contents of this.state.template, while the original one never changes, so your state becomes

state = {
    template: {status_data: ...},
    status_data: ...,
    ...
}
JulienD
  • 7,102
  • 9
  • 50
  • 84
  • You probably want `Object.assign({}, this.state.template, { status_data })` so that you are [not modifying state directly](https://reactjs.org/docs/state-and-lifecycle.html#do-not-modify-state-directly) – Craig Ayre Oct 20 '17 at 11:13
  • @JulienD thanks. But this doesn't solve the problem :9 – Muhammad Arslan Aslam Oct 20 '17 at 11:16
  • So now the state is changing correctly, but the view does not, so you need to show us the view here, we hardly guess without. – JulienD Oct 20 '17 at 11:22
  • I think, in the `renderActiveStatuses` function, `key={status.id}` has something to do with not updating the view. As new statuses doesn't have a key, at least note before going to the server. @JulienD could that be an issue? – Muhammad Arslan Aslam Oct 20 '17 at 11:37
  • Yes, that is the alternative answer I just wrote. – JulienD Oct 20 '17 at 11:37
  • This should use [the callback version of `setState`](https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous). – ggorlen Jan 11 '23 at 17:44