Reputation: 17
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
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
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
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
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