dimanche 28 juin 2020

React: trying to check/uncheck checkbox

I'm trying to make an obligatory Todo app to help with my React learning . The behavior I'm going for is multiple Todo lists, where you select a todo name and the list of todo items for that show. Select a different todo name and it's list todo items show, etc (like wunderlist/msft todo). For now it's using static json where each item has a child array.

I'm trying to check/uncheck a checkbox in order to mark the todo as done. My problem is that when a click the checkbox it doesn't update until a click away and then click back. What do I need to do to get it to update immediately?

code sandbox: https://codesandbox.io/s/github/cdubone/todo-react-bootstrap?file=/src/App.js

Here's the relevant code:

The function:

const updateChecked = todo => {
todo.IsChecked = !todo.IsChecked;
};

Properties on the component:

onChange={() => updateChecked(todo)}
isChecked={todo.IsChecked}

The input in the component:

<input
type="checkbox"
id={props.id}
name={props.id}
value={props.title}
checked={props.isChecked}
onChange={props.onChange}
/>

Here's the rest of the code if this helps -

JSON:

const TodoData = [
    {
        "Id": 1,
        "Title": "Groceries",
        "TodoList": [
            {
                "Id": 1,
                "Title": "Apples",
                "IsChecked": false
            },
            {
                "Id": 2,
                "Title": "Oranges",
                "IsChecked": false
            },
            {
                "Id": 3,
                "Title": "Bananas",
                "IsChecked": true
            }
        ]
    },
    {
        "Id": 2,
        "Title": "Daily Tasks",
        "TodoList": [{
            "Id": 11,
            "Title": "Clean Kitchen",
            "IsChecked": false
        },
        {
            "Id": 12,
            "Title": "Feed Pets",
            "IsChecked": false
        },
        {
            "Id": 13,
            "Title": "Do Stuff",
            "IsChecked": false
        }]
    },
    {
        "Id": 3,
        "Title": "Hardware Store",
        "TodoList": []
    },
    {
        "Id": 4,
        "Title": "Costco",
        "TodoList": [{
            "Id": 21,
            "Title": "Diapers",
            "IsChecked": false
        },
        {
            "Id": 22,
            "Title": "Cat Food",
            "IsChecked": false
        },
        {
            "Id": 23,
            "Title": "Apples",
            "IsChecked": false
        },
        {
            "Id": 24,
            "Title": "Bananas",
            "IsChecked": false
        }]
    },
    {
        "Id": 5,
        "Title": "Work",
        "TodoList": [
            {
                "Id": 34,
                "Title": "TPS Reports",
                "IsChecked": true
            }
        ]
    }
]

export default TodoData;

App.js

function App() {
  const [todoData, setTodoData] = useState([]);
  const [activeTodoList, setActiveTodoList] = useState(null);
  const [todoListFormValue, setTodoListFormValue] = useState('');
  const [todoListEditorToOpen, setTodoListEditorToOpen] = useState('');
  const [todoListToAdd, setTodoListToAdd] = useState('');
  const [todoItemToAdd, setTodoItemToAdd] = useState('');

  useEffect(() => {
    setTodoData(TodoData);
    setActiveTodoList(TodoData[0]);
  }, []);

  const addTodoList = e => {
    e.preventDefault();
    const newItem = {
      "Id": randomNumber(),
      "Title": todoListToAdd,
      "TodoList": []
    };
    const newArray = [...todoData, newItem];
    setTodoData(newArray);
    setActiveTodoList(newItem);
    setTodoListToAdd('');
  };

  const deleteTodoList = (todolist) => {
    const newArray = todoData.filter(x => x !== todolist);
    setTodoData(newArray);
    setActiveTodoList(newArray[0]);
  };

  const saveEditTodoListTitle = (e) => {
    e.preventDefault();
    const id = Number(e.currentTarget.dataset.id);
    const result = todoData.find(x => x.Id === id);
    result.Title = todoListFormValue;
    closeTodoListTitleEditor();
  };

  const openTodoListTitleEditor = (todo) => {
    setTodoListEditorToOpen(todo.Id);
    setTodoListFormValue(todo.Title);
  };

  const closeTodoListTitleEditor = () => {
    setTodoListEditorToOpen('');
  };

  const addTodoHandler = e => {
    e.preventDefault();
    const newItem = {
      "Id": randomNumber(),
      "Title": todoItemToAdd,
      "IsChecked": false
    };
    const newArray = [...activeTodoList.TodoList, newItem];
    activeTodoList.TodoList = newArray;
    setTodoItemToAdd('');
  };

  const updateChecked = todo => {
    todo.IsChecked = !todo.IsChecked;
  };

  const deleteTodo = (todo) => {
    const newArray = activeTodoList.TodoList.filter(x => x !== todo);
    console.log(newArray);
    activeTodoList.TodoList = newArray;
    setActiveTodoList(activeTodoList);
  };

  const randomNumber = () => {
    return Math.floor(Math.random() * 200) + 100;
  };

  const updateEditTodoList = e => {
    setTodoListFormValue(e.target.value);
  }

  const updateAddList = e => {
    setTodoListToAdd(e.target.value);
  };

  const updateAddTodo = e => {
    setTodoItemToAdd(e.target.value);
  };

  return (
    <div className="App">
      <div className="container">
        <Header />
        <div className="row">
          <ul className="list-group col-sm-4 offset-sm-1">
            {todoData.map(todo => (
              <TodoListItem key={todo.Id}//Not a prop
                onClick={() => setActiveTodoList(todo)}
                title={todo.Title}
                length={todo.TodoList.length}
                selected={activeTodoList.Id === todo.Id}
                deleteClick={() => deleteTodoList(todo)}
                editClick={() => openTodoListTitleEditor(todo)}
                saveEdit={saveEditTodoListTitle}
                closeEditClick={() => closeTodoListTitleEditor()}
                editorOpen={todoListEditorToOpen === todo.Id}
                updateForm={updateEditTodoList}
                value={todoListFormValue}
                id={todo.Id} />
            ))}
            <TextInput
              onSubmit={addTodoList}
              placeholder="Add New List"
              value={todoListToAdd}
              onChange={updateAddList} />
          </ul>
          <div className="col-sm-6">
            {activeTodoList && (
              <ul className="list-group">
                <h2>{activeTodoList.Title} List</h2>
                {activeTodoList.TodoList.map(todo => (
                  <TodoItem key={todo.Id} //not a prop
                    id={todo.Id}
                    title={todo.Title}
                    onChange={() => updateChecked(todo)}
                    isChecked={todo.IsChecked}
                    deleteClick={() => deleteTodo(todo)} />
                ))}
              </ul>
            )}
            <TextInput
              onSubmit={addTodoHandler}
              placeholder="Add New Todo"
              value={todoItemToAdd}
              onChange={updateAddTodo} />
          </div>
        </div>
      </div>
    </div>
  );
}

export default App;

TodoItem.js

import React from 'react';
import { PencilIcon, XIcon } from '@primer/octicons-react';
// import PropTypes from "prop-types";

function TodoItem(props) {

    let wrapperClass = "list-group-item list-group-item-action todo-item";
    if (props.isChecked === true) {
        wrapperClass += " done";
    }

    return (
        <li className={wrapperClass}>
            <input
                type="checkbox"
                id={props.id}
                name={props.id}
                value={props.title}
                checked={props.isChecked}
                onChange={props.onChange}
            />
            <label htmlFor={props.id}>{props.title}</label>
            <div className='modify-todo'>
                <button><PencilIcon size={16} /></button>
                <button data-parent={props.id} onClick={props.deleteClick}><XIcon size={16} /></button>
            </div>
        </ li>
    )
}

export default TodoItem;



Aucun commentaire:

Enregistrer un commentaire