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>
);
}
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
defaultValueat the same time as you usevalueandonChange. These are mutually exclusive.defaultValueis for non-controlled inputs whilevalueandonChangeare for controlled inputs.Get rid of
defaultValueand do this:I believe the change you need to do is simple.
It should just be to change this:
to this:
The alternative is to use an uncontrolled component (just
defaultValue, novalue/onChange) and use a ref (and noeditedTitlestate):and then onSave you'd do: