Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

setState array of objects without changing objects order

In state, I have an array of list objects and would like to toggle the displayAddTaskForm. Every time I set state, the list that gets clicked gets rearranged and moves to the front of the lists on the UI. I believe I know why this is happening, just not sure of the solution. When I setState in toggleAddTaskForm, toggledList is first and then the other lists. How do I update the toggledList without changing the order of the lists. Here is the code.

this.state = {
      lists: [
        {
          id: 1,
          header: "To Do",
          displayAddTaskForm: false,
        },
        {
          id: 2,
          header: "Working on It",
          displayAddTaskForm: false,
        },
        {
          id: 3,
          header: "Taken Care Of",
          displayAddTaskForm: false,
        }
      ],
      toggleAddTaskForm: (list) => {
        const toggledList = {...list, displayAddTaskForm: !list.displayAddTaskForm}
        const otherLists = this.state.lists.filter(l => l.id !== list.id)
        this.setState({
          lists: [toggledList, ...otherLists]
        })
      },
}
like image 443
Keith Avatar asked Oct 29 '25 00:10

Keith


2 Answers

Putting function in the state is not a common thing. I think you need to seperate that function from state.

You can easily toggle items using Array.map() and checking the clicked item id with the item id. This will not change the order of items.

You can use the following code to toggle only one item:

class App extends Component {
  constructor() {
    super();
    this.state = {
      lists: [
        {
          id: 1,
          header: "To Do",
          displayAddTaskForm: false
        },
        {
          id: 2,
          header: "Working on It",
          displayAddTaskForm: false
        },
        {
          id: 3,
          header: "Taken Care Of",
          displayAddTaskForm: false
        }
      ]
    };
  }
  handleClick = id => {
    let newList = this.state.lists.map(item => {
      if (item.id === id) {
        return {
          ...item,
          displayAddTaskForm: !item.displayAddTaskForm
        };
      } else {
        return {
          ...item,
          displayAddTaskForm: false
        };
      }
    });

    this.setState({ lists: newList });
  };

  render() {
    return (
      <div>
        <ul>
          {this.state.lists.map(({ id, header, displayAddTaskForm }) => {
            return (
              <li key={id} onClick={() => this.handleClick(id)}>
                {header} - Toggle Value: {displayAddTaskForm ? "true" : "false"}
              </li>
            );
          })}
        </ul>
      </div>
    );
  }
}

Playground

Or if you want to be able to toggle every item, you can change the handleClick function like this:

  handleClick = id => {
    let newList = this.state.lists.map(item => {
      if (item.id === id) {
        return {
          ...item,
          displayAddTaskForm: !item.displayAddTaskForm
        };
      } else {
        return {
          ...item
        };
      }
    });

    this.setState({ lists: newList });
  };

Playground

like image 94
SuleymanSah Avatar answered Oct 31 '25 14:10

SuleymanSah


You could find the index of the list, copy the lists, and insert the modified list into the new lists array at the same index.

toggleAddTaskForm: (list) => {
  const toggledList = {...list, displayAddTaskForm: !list.displayAddTaskForm}
  const newLists = [...this.state.lists];
  newLists[this.state.lists.indexOf(list)] = toggledList;
  this.setState({
    lists: newLists
  })
}
like image 41
UncleDave Avatar answered Oct 31 '25 14:10

UncleDave



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!