Reputation: 79
Todo app In my Todo app, I have a Todos List data, and the user can edit todo titles and save or cancel. Saving will change the data in the state, and in case of canceling, the title will remain the same. That's why I decided to have another state in my Task component: const [editedTitle, setEditedTitle] = useState(title); for saving the value during input onChange, and initializing the value from the prop. I know that using props in the initial state is an antipattern in React. How can I achieve the same result without using the prop in the state?
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
function Task({ id, title, done, onSaveTodo, onDeleteTodo, onDoneTodo }) {
const [isEditing, setIsEditing] = useState(false);
const [editedTitle, setEditedTitle] = useState(title);
let todoContent;
if (isEditing) {
todoContent = (
<>
<input
value={editedTitle}
defaultValue={title}
onChange={(e) => setEditedTitle(e.target.value)}
/>
<button
onClick={() => {
setIsEditing(false);
setEditedTitle(title);
}}
>
Cancel
</button>
<button
onClick={() => {
setIsEditing(false);
onSaveTodo(id, editedTitle);
}}
>
Save
</button>
</>
);
} else {
todoContent = (
<>
<label>{title}</label>{" "}
<button onClick={() => setIsEditing((prevState) => !prevState)}>
Edit
</button>
</>
);
}
return (
<li>
<input
type="checkbox"
checked={done}
onChange={(e) => onDoneTodo(id, e.target.checked)}
/>{" "}
{todoContent}
<button onClick={() => onDeleteTodo(id)}>Delete</button>
</li>
);
}
Upvotes: 0
Views: 75
Reputation: 55792
Regarding "don't put props in state" - it's a general rule, emphasis on general. When you are "editing" a prop (and using controlled state in your component) you have no choice but to put props in state like you have.
This is how to use a controlled component:
It's inappropriate to use defaultValue
at the same time as you use value
and onChange
. These are mutually exclusive. defaultValue
is for non-controlled inputs while value
and onChange
are for controlled inputs.
Get rid of defaultValue
and do this:
I believe the change you need to do is simple.
It should just be to change this:
<input
value={editedTitle}
defaultValue={title}
onChange={(e) => setEditedTitle(e.target.value)}
/>
to this:
<input
value={editedTitle}
onChange={(e) => setEditedTitle(e.target.value)}
/>
The alternative is to use an uncontrolled component (just defaultValue
, no value
/onChange
) and use a ref (and no editedTitle
state):
const inputRef = useRef();
<input defaultValue={title} ref={inputRef}/>
and then onSave you'd do:
saveToDo(id,inputRef.current.value)
Upvotes: 1