EliasSDA
EliasSDA

Reputation: 17

update item with axios on click in reactjs

I have a todo list and I'm trying to update the completed/not-completed boolean on click via axios. I'm not sure how to best approach this.

Unfortunately I'm a total beginner and I lack the general react understanding to find the right approach.

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      todoList: []
      }
    };

    componentDidMount() {
      this.refreshList();
    }

  refreshList = () => {
    axios
      .get("http://localhost:8000/api/todos/")
      .then(res => this.setState({ todoList: res.data }))
      .catch(err => console.log(err));
  };

  handleCheck = () => {
    //toggle the 'completed' property and then update
  };

  update = item => {
      axios
        .put(`http://localhost:8000/api/todos/${item.id}/`, item)
        .then(res => this.refreshList())
        .catch(err => console.log(err));
      return;
  };


  renderItems = () => {
    const newItems = this.state.todoList
    return newItems.map(item => (
      <div key={item.id}>
        <h6>{item.title}</h6>
        <Form>
        <button onClick={this.handleCheck(item)} type="submit" ></button>
        </Form>
      </div>
    ));
  };

  render() {
    return (
      <h4>ToDos</h4>
      {this.renderItems()}
    );
  }
}

todo data-structure:

{
   "id": 1,
   "title": "Todo #1",
   "description": "Lorem Ipsum",
   "completed": false
  }

EDIT:

Update: I managed to successfully update the "completed" boolean with the commented line, updating the value directly. But I understand that is the non-ideal way, but when trying to do the same through setState I get a Unexpected Token Error, "Unexpected token, expected , (39:27)". How can I edit setState to function properly?

  handleCheck = (item, index) => {

    this.setState({todoList[index].completed : !todoList[index].completed});
    // this.state.todoList[index].completed = !this.state.todoList[index].completed;
    this.update(item);
  };

Upvotes: 0

Views: 3504

Answers (4)

Junius L
Junius L

Reputation: 16152

Pass the index of your todo to handleCheck function and toggle the completed value.

handleCheck = (item, index) => {
  //whatever needs to happen here to updated the 'completed' property
  this.setState({
    todoList: [...this.state.todoList.slice(0, index),
    Object.assign({}, this.state.todoList[index], {completed: !this.state.todoList[index].completed}),
    ...this.state.todoList.slice(index + 1)
    ]
  });

  this.update(item);
};

renderItems = () => {
  const newItems = this.state.todoList || [];
  return newItems.map((item, index) => (
    <div key={item.id}>
      <h6>{item.title}</h6>
      <Form>
        <button onClick={() => this.handleCheck(item, index)} type="submit" ></button>
      </Form>
    </div>
  ));
};

Simplified version, create a copy of your todoList and update todo using the index.

handleCheck = (item, index) => {

  const copy = JSON.parse(JSON.stringify(this.state.todoList));

  copy[index].completed = !copy[index].completed;

  this.setState({
    todoList: copy
  });

  this.update(item);
};

DEMO

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.0/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.21.1/babel.min.js"></script>
<div id="root"></div>

<script type="text/babel">

class App extends React.Component {
  constructor() {
    super();
    this.state = {
      name: 'React',
      todoList: [
        {
          "id": 1,
          "title": "Todo #1",
          "description": "Lorem Ipsum",
          "completed": false
        },
        {
          "id": 2,
          "title": "Todo #2",
          "description": "Lorem Ipsum",
          "completed": false
        },
        {
          "id": 3,
          "title": "Todo #3",
          "description": "Lorem Ipsum",
          "completed": false
        }
      ]
    };
  }

  handleCheck = (item, index) => {

    const copy = JSON.parse(JSON.stringify(this.state.todoList));

    copy[index].completed = !copy[index].completed;

    this.setState({
      todoList: copy
    });

    // this.update(item);
  };

  renderItems = () => {
    const newItems = this.state.todoList || [];
    return newItems.map((item, index) => (
      <div key={item.id}>
        <h6>
        {item.completed ? <strike>{item.title}</strike> : item.title}</h6>
          <button onClick={() => this.handleCheck(item, index)} >Submit</button>
      </div>
    ));
  };

  render() {
    return (
      <div>
       
        <p>
          Todos
        </p>
        {this.renderItems()}
      </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById("root"));
</script>

Upvotes: 2

EliasSDA
EliasSDA

Reputation: 17

This solution was suggested by Junius L. but I had to alter/simplify the handleCheck function. So here is the altered solution and my learnings, maybe this helps someone. Also please comment in case my conclusions are incorrect.

Learning #1: Do not update state without using setState() Learning #2: You cannot update a property at the second level of state. Instead, copy state it into a local variable, make the needed updates and then set the entire state new.

handleCheck = (item, index) => {

    const items = this.state.todoList
    items[index].completed = !items[index].completed
    this.setState({todoList:items});
    this.update(item);

  };

renderItems = () => {
  const newItems = this.state.todoList || [];
  return newItems.map((item, index) => (
    <div key={item.id}>
      <h6>{item.title}</h6>
      <Form>
        <button onClick={() => this.handleCheck(item, index)} type="submit" ></button>
      </Form>
    </div>
  ));
};

Upvotes: 0

Omer Khan
Omer Khan

Reputation: 457

You haven't called that update function anywhere, while you've called that handle click function on button's click.

So, simply take items as a parameter in handle check function and call the update function from there, it'll work

handleCheck = (item) => {
  this.update(item)
}

Upvotes: 1

pr0p
pr0p

Reputation: 2358

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      todoList: []
      }
    };

    componentDidMount() {
      this.refreshList();
    }

  refreshList = () => {
    axios
      .get("http://localhost:8000/api/todos/")
      .then(res => this.setState({ todoList: res.data }))
      .catch(err => console.log(err));
  };

  handleCheck = (item) => {
       this.update(item)
       // Make your backend do the update
  };

  update = item => {
      axios
        .put(`http://localhost:8000/api/todos/${item.id}/`, item)
        .then(res => this.refreshList())
        .catch(err => console.log(err));
      return;
  };


  renderItems = () => {
    const newItems = this.state.todoList
    return newItems.map(item => (
      <div key={item.id}>
        <h6>{item.title}</h6>
        <Form>
       // See this line ( how to bind functions )
        <button onClick={this.handleCheck.bind(this, item)} type="submit" ></button>
        </Form>
      </div>
    ));
  };

  render() {
    return (
      <h4>ToDos</h4>
      {this.renderItems()}
    );
  }
}

Upvotes: 1

Related Questions