Mjuice
Mjuice

Reputation: 1288

Can't use checkbox correctly to update state

I am using a checkbox that should update a todo item's completed status via the component's state. When I click the todo box it doesn't change state, but if I click it two times, it does update the state. Since I have to click it two times, that means when the box is checked this.state.done === false and when it is uncheck this.state.done === true. I don't know why it doesn't flip the state on the first click. The checkbox is controlled by handleDoneChange(). Could someone tell me why the checkbox doesn't change the state on the first click?

class ShowTodo extends Component {

    static contextTypes = {
        router: PropTypes.object
    };

    constructor(props) {
        super(props);

        this.state = {
            descriptionChanged: false,
            newDescription: '',
            newTitle: '',
            done: false
        };


        this.handleDescriptionChange = this.handleDescriptionChange.bind(this);
        this.handleDeleteClick = this.handleDeleteClick.bind(this);
        this.changeButtons = this.changeButtons.bind(this);
        this.handleSaveClick = this.handleSaveClick.bind(this);
        this.handleUndoClick = this.handleUndoClick.bind(this);
        this.handleTitleChange = this.handleTitleChange.bind(this);
        this.handleDoneChange = this.handleDoneChange.bind(this);
    }

    componentWillMount() {
        this.props.fetchTodo(this.props.params.id).then(() => {
            this.setState({
                newDescription: this.props.todo.description,
                newTitle: this.props.todo.title,
                done: this.props.todo.completed
            });
            console.log("This is the todo's starting completed status: ", this.state.done);
        });
    }

    render() { 

        const { todo } = this.props;

        if (!todo) {
            return (
                <h3>Loading...</h3>
            );
        }

        return (
            <div className="input-group">
                <Link to="/todos_index">Back</Link>
                <h3>Title</h3>
                <input 
                    type="text" 
                    className="form-control"
                    value={this.state.newTitle}
                    onChange={this.handleTitleChange} />
                <textarea 
                    className="form-control" 
                    value={this.state.newDescription}
                    onChange={this.handleDescriptionChange}>
                </textarea>
                <span className="input-group-addon">
                    <input type="checkbox"
                      onClick={this.handleDoneChange} />
                </span>
                <span className="input-group-btn">
                    {this.changeButtons()}
                    <button onClick={this.handleDeleteClick} className="btn btn-danger pull-xs-right">Delete Post</button>
                </span>


            </div>
        );
    }

    changeButtons() {
        if (!this.state.descriptionChanged) {
            return null;
        } else {

            return [
            <button 
              className="btn btn-default"
              onClick={this.handleSaveClick}
              >Save</button>,
            <button 
              className="btn btn-default"
              onClick={this.handleUndoClick}
              >Undo</button>
           ];
        }
    }

    handleDescriptionChange(event) {
        this.setState({
            descriptionChanged: true,
            newDescription: event.target.value
        });
        console.log('New description in state: ', this.state.newDescription);
    }

    handleTitleChange(event) {
        this.setState({
            descriptionChanged: true,
            newTitle: event.target.value
        });
    }

    handleDoneChange(event) { //This isn't updating the done status

        this.setState({
            done: !this.state.done
        });

        var id = this.props.params.id;

        var props = {
            completed: this.state.done
        };

        console.log(props);

        this.props.updateTodo(id, JSON.stringify(props));

    }

    handleDeleteClick(event) {
        this.props.deleteTodo(this.props.params.id).then(() => {
            this.context.router.push('/todos_index');
        });
    }

    handleSaveClick(event) {
        var id = this.props.params.id;
        var props = {
            title: this.state.newTitle,
            description: this.state.newDescription
        };
        this.props.updateTodo(id, JSON.stringify(props)).then(() => {
            this.context.router.push('/todos_index');
        });
    }

    handleUndoClick() {
        this.setState({
            descriptionChanged: false,
            newTitle: this.props.todo.title,
            newDescription: this.props.todo.description

        });
    }
}

function mapStateToProps(state) {
    return { todo: state.todos.todo };
}

export default connect(mapStateToProps, { fetchTodo, updateTodo, deleteTodo })(ShowTodo);

Upvotes: 0

Views: 1085

Answers (1)

Jake Haller-Roby
Jake Haller-Roby

Reputation: 6427

Checkboxes should use onChange rather than onClick.

EDIT: There's another issue.

Your component is working correctly. Your debug console.log is not.

this.setState is asynchronous, so you won't see the changes in the following lines of code. If you want to do something after the state has finished changing, you should pass it as a callback to the setState function:

this.setState({
        descriptionChanged: true,
        newDescription: event.target.value
    }, function(){         
           console.log('New description in state: ', this.state.newDescription);
    }
});

Upvotes: 2

Related Questions