2

I have implemented simple one page Todo app with add, edit and delete functionality. Most of the part has been completed but i am stuck at one point. When I click on edit button I want to fill the top Input textbox with the value of clicked row and then after editing I want to update in the list view. Could you please guide me on that.

I have created one fiddle to explain it.

Here is my repository : https://github.com/JigneshRaval/react-todo-app

JSFiddle URL : https://jsfiddle.net/jigneshraval/3b3dabr6/20/

Also pasted working code below which can be run by clicking "Run code snippet" button.

const Title = ({ todoCount }) => {
    return (
        <div>
            <div>
                <h1>Todos: ({todoCount})</h1>
            </div>
        </div>
    );
}

var randomString = function (length) {
    var text = "";
    var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
    for (var i = 0; i < length; i++) {
        text += possible.charAt(Math.floor(Math.random() * possible.length));
    }
    return text;
}


class TodoForm extends React.Component {
    // Input Tracker
    constructor(props) {
        super(props);
        console.log(props.editTodo.title);
        this.state = { value: props.editTodo.title };
        this.input;
        this.handleSubmit = this.handleSubmit.bind(this);
    }

    handleChange(event) {
        this.setState({
            value: event.target.value
        })
    }

    handleSubmit(event) {
        event.preventDefault();
        console.log(event.target);
        let newTodoTitle = event.target.querySelector('input');
        this.props.addTodo(this.input.value);
        newTodoTitle.value = '';
    }

    renderAddTodoForm() {
        // Return JSX
        return (
            <form onSubmit={this.handleSubmit}>
                {/*<form onSubmit={(e) => {
                e.preventDefault();
                this.props.addTodo(this.input.value);
                this.input.value = '';
            }}>*/}
                <input className="form-control col-md-12 add-form"
                    ref={(input) => this.input = input}
                    value={this.state.value}
                    onChange={this.handleChange.bind(this)} />
                <br />
            </form>
        );
    }

    render() {
        return this.renderAddTodoForm();
    }
}


// 3. Single Todo
// ================================
const Todo = ({ todo, remove, edit }) => {
    // Each Todo
    return (
        <li className="list-group-item">
            <a href="#" data-todoid={todo._id} name="todoTitle" data-toggle="tooltip" data-placement="top" title="Click on item to delete.">{todo.title} =  {todo.status}</a>

            <button className="btn btn-danger float-right" onClick={() => { remove(todo._id) }}>Delete</button>
            <button className="btn btn-primary float-right" onClick={() => { edit(todo._id) }}>Edit</button>
        </li>);
}

// 2. Todo List
// ================================
const TodoList = ({ todos, remove, edit }) => {
    // Map through the todos
    const todoNode = todos.map((todo) => {
        return (<Todo todo={todo} key={todo._id} remove={remove} edit={edit} />)
    });

    return (<ul className="list-group" style={{ marginTop: '30px' }}>{todoNode}</ul>);
}

// 1. Main TODO App
// ================================
class TodoApp extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            data: [
                { "title": "Buy Milk", "status": "pending", "_id": "3QACIouhZZlhmm6T" },
                { "title": "Buy new computer book", "status": "pending", "_id": "9Xz2MSHIeh87WMgF" },
                { "title": "Fetch Money", "status": "pending", "_id": "DZvP2o5Dd4t9J3Ax" },
                { "title": "Play new game", "status": "pending", "_id": "Dp3J6BacTeG8ijV8" },
                { "title": "new 2", "status": "pending", "_id": "ECDUmbjOt4vtOKSv" },
                { "title": "sdfsdafsaf", "status": "pending", "_id": "GwHBilbjsbXipQuI" },
                { "title": "New 10", "status": "pending", "_id": "HGWGsNEVFNXIGZ8S" },
                { "title": "ppp", "status": "pending", "_id": "IExYBTFUFkGoHqyN" }
            ],
            isEditing: false,
            editTodo: {}
        }
    }

    // Add Todo
    addTodo(value) {
        this.state.data.push({ "title": value, "status": "pending", "today": { "$$date": Date.now() }, "_id": randomString(16) });
        this.setState({ data: this.state.data });
    }

    // Edit Todo
    editTodo(todoId) {
        const remainder = this.state.data.filter((todo) => {
            if (todo._id === todoId) return todo;
        });
        this.setState({ isEditing: true, editTodo: remainder[0] });
    }

    // Remove Todo
    removeTodo(id) {
        // Filter all todos except the one to be removed
        const remainder = this.state.data.filter((todo) => {
            if (todo._id !== id) return todo;
        });
        this.setState({ data: remainder });
    }


    handleInputChange(newValue) {
        console.log(newValue);
    }

    render() {
        return (
            <main>
                <div className="container">
                    <Title todoCount={this.state.data.length} />
                    <TodoForm isEditing={this.state.isEditing} handleInputChange={this.handleInputChange.bind()} editTodo={this.state.editTodo} addTodo={this.addTodo.bind(this)} />
                    <TodoList
                        todos={this.state.data}
                        remove={this.removeTodo.bind(this)}
                        edit={this.editTodo.bind(this)}
                    />
                </div>
            </main>
        )
    }
}

ReactDOM.render(
    <TodoApp />,
    document.getElementById('app')
);
<script src="https://unpkg.com/react@16.2.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.2.0/umd/react-dom.development.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" />

<div id="app"></div>

Could anyone can please guide me on that.

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
Jignesh Raval
  • 587
  • 7
  • 15
  • I think you split to much. If you just create a parent component with the form and create a component like todo and load all of them in your parent component, you can use a normal pattern to update a parents state. – Janick Fischer Feb 19 '18 at 13:29
  • Thanks Aditya and Janick for your help. I tried solution provided by Aditya with some logic and now I am able to Edit record properly. Updated code and working example is availbale on CodePen. https://codepen.io/jigneshraval/pen/XZEZOw If, you could review my code and correct me if i am wrong at any place or suggest me better solution then it will your great help. Thanks again. – Jignesh Raval Feb 20 '18 at 14:52

2 Answers2

1

You need to add a lifecycle hook as follows to your ToDoForm component like below. But add condition to setState so that it is called only when next is different from this.state.value

componentWillReceiveProps (next) {
   this.setState({value: next.editTodo.title});
}

This will populate the text in your form. You can make further changes following this line.

Please see we should use componentWillReceiveProps instead of componentWillUpdate in order to setState

Aditya
  • 861
  • 5
  • 8
  • Thank you @Aditya for your time and reply, I will try your suggestions and will update you. – Jignesh Raval Feb 19 '18 at 13:55
  • Hi Aditya, I tried your solution and implemented some logic to diffrentiate Add/Edit functionality and now it is working. But after editing value textbox doesn't clear the value and always remain in edit mode with last item id. Could you please review the code and guide me. Updated Jsfiddle link : https://jsfiddle.net/jigneshraval/3b3dabr6/59/ – Jignesh Raval Feb 20 '18 at 14:02
  • I have also placed same updated example on Code Pen: CodePen link : https://codepen.io/jigneshraval/pen/XZEZOw – Jignesh Raval Feb 20 '18 at 14:12
  • Hi Jignesh just saw your comment. Will help you with it in an hour or two – Aditya Feb 21 '18 at 14:15
0

I would split everything in only 2 components: TodoApp and Todo.


TodoApp has a form and an array with all its childs informations.

Todo has its functions and its information to show.


Then use a normal pattern like in this answer: How to update parent's state in React?.

I hope this helps.

Janick Fischer
  • 651
  • 7
  • 17