2
\$\begingroup\$

I have a working useless todo app created with ReactJS. I'm just wondering if my code can be improved.

This app can add a todo item and it can display editable list of todo items.

This is my ReactJS code.

var TodoApp = React.createClass({
    getInitialState: function() {
        return {items: []};
    },
    addItem: function(item) {
        var allItems = this.state.items.concat([item]);
        this.updateItems(allItems);
    },
    editItem: function(editedItem) {
        var allItems = this.state.items.map(function(item) {
            if (item.id !== editedItem.id) {
                return item;
            }

            return editedItem;
        });

        this.updateItems(allItems);
    },
    updateItems: function(items) {
        this.setState({items: items});
    },
    render: function() {
        return (
            <div className="todo-app">
                <TodoForm onFormSubmit={this.addItem}/>
                <TodoList items={this.state.items} onItemEdit={this.editItem}/>
            </div>
        );
    }
});

var TodoForm = React.createClass({
    getInitialState: function() {
        return {item: {id: 0, title: '', description: ''}};
    },
    render: function() {
        return (
            <form onSubmit={this.handleSubmit}>
                <input type="hidden" ref="id" value={this.state.item.id} onChange={this.handleUserInput}/>
                <input type="text" ref="title" value={this.state.item.title} onChange={this.handleUserInput}/>
                <input type="text" ref="description" value={this.state.item.description} onChange={this.handleUserInput}/>
                <input type="submit" value="Add"/>
            </form>
        );
    },
    handleSubmit: function(e) {
        e.preventDefault();

        this.props.onFormSubmit(this.state.item);
        this.setState({item: {id: 0, title: '', description: '', done: false}});
    },
    handleUserInput: function() {
        this.setState({item: {
            id: this.refs.id.value,
            title: this.refs.title.value,
            description: this.refs.description.value,
            done: false
        }});
    }
});

var TodoList = React.createClass({
    render: function() {
        var component = this;

        var itemNodes = this.props.items.map(function(item) {
            return <TodoListItem key={item.title} item={item} onFormSubmit={component.props.onItemEdit}/>
        });

        return (
            <ul>{itemNodes}</ul>
        );
    }
});

var TodoListItem = React.createClass({
    getInitialState: function() {
        return {
            id: this.props.item.id,
            done: this.props.item.done,
            title: this.props.item.title,
            description: this.props.item.description,
        };
    },
    render: function() {
        return (
            <li>
                <form onSubmit={this.handleFormSubmit}>
                    <input type="hidden" ref="id" value={this.state.id}/>
                    <input type="checkbox" ref="done" value={this.state.done} onChange={this.handleUserInput}/>
                    <input type="text" ref="title" value={this.state.title} onChange={this.handleUserInput}/>
                    <input type="text" ref="description" value={this.state.description} onChange={this.handleUserInput}/>
                    <input type="submit" value="Save"/>
                </form>
            </li>
        );
    },
    handleUserInput: function() {
        this.setState({
            id: this.refs.id.value,
            done: this.refs.done.checked,
            title: this.refs.title.value,
            description: this.refs.description.value
        });
    },
    handleFormSubmit: function(e) {
        e.preventDefault();
        this.props.onFormSubmit(this.state);
    }
});

ReactDOM.render(<TodoApp/>, document.getElementById('todoListBox'));

ReactJS said in their docs that components should be stateless as possible. I'm aware that in the TodoListItem component, It has a state based from its props. I did that so I can change the form inside TodoListItem.

Is there any other possible solutions to do this other than their two way binding add-on?

\$\endgroup\$
0
\$\begingroup\$

So I managed to eliminate the TodoListItem's state, which is based on its props, by reusing the TodoForm component with a minor tweak for an inline form.

This is the TodoListItem and TodoForm component now.

var TodoForm = React.createClass({
    getInitialState: function() {
        return {item: {
            id: this.props.todoId,
            title: this.props.todoTitle,
            description: this.props.todoDescription,
            done: this.props.todoDone
        },
        isAnInlineForm: this.props.isAnInlineForm};
    },
    render: function() {
        return (
            <form onSubmit={this.handleSubmit}>
                <input type="checkbox" ref="done" checked={this.state.item.done} onChange={this.handleUserInput}/>
                <input type="hidden" ref="id" value={this.state.item.id} onChange={this.handleUserInput}/>
                <input type="text" ref="title" value={this.state.item.title} onChange={this.handleUserInput}/>
                <input type="text" ref="description" value={this.state.item.description} onChange={this.handleUserInput}/>
                <input type="submit" value="Save"/>
            </form>
        );
    },
    handleSubmit: function(e) {
        e.preventDefault();

        this.props.onFormSubmit(this.state.item);

        if (!this.state.isAnInlineForm) {
            this.setState({item: {id: 0, title: '', description: '', done: false}});
        }
    },
    handleUserInput: function() {
        this.setState({item: {
            id: this.refs.id.value,
            title: this.refs.title.value,
            description: this.refs.description.value,
            done: this.refs.done.checked
        }});
    }
});


var TodoListItem = React.createClass({
    render: function() {
        return (
            <li>
                <TodoForm onFormSubmit={this.handleFormSubmit}
                          todoId={this.props.item.id}
                          todoDone={this.props.item.done}
                          todoTitle={this.props.item.title}
                          todoDescription={this.props.item.description}
                          isAnInlineForm={true}
                    />
            </li>
        );
    },
    handleFormSubmit: function(item) {
        this.props.onFormSubmit(item);
    }
});
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.