As a React beginner, I want to build a simple to-do list app.
There is a list of some tasks, and list items are generated by list.map().
And If <label>
is double-clicked, turn that label into <input type="text">
so the user can modify the task.
So I tried using some states in the Parent component and passed props to the child component like this.
export default function App() {
let list = [
{
"id": "id-1"
"title": "get some sleep"
},
{
"id": "id-2"
"title": "check the mailbox"
},
{
"id": "id-3"
"title": "ask question to stackoverflow"
}
]
const [editTargetTaskId, setEditTargetTaskID] = useState<string>(``);
const [editMode, setEditMode] = useState<boolean>(false);
const toggleEditMode = (editMode: boolean) =>
editMode ? setEditMode(false) : setEditMode(true);
// When double click the label, change states
const triggerEdit = (e: React.MouseEvent<HTMLLabelElement>) => {
setEditTargetTaskID(e.currentTarget.parentElement?.id || ``);
toggleEditMode(false);
};
const editTask = (
newInput: string,
list: Task[],
editTaskId: string,
editMode: boolean
) => {
setList(
list.map((task: Task) => {
if (task.id === editTaskId) {
return { ...task, title: newInput };
} else return task;
})
);
toggleEditMode(editMode);
};
return (
<ul>
{list.map((task: Task) => (
<li key={task.id} id={task.id}>
<TaskListItem
task={task}
list={list}
isEdit={editMode}
triggerEdit={triggerEdit}
editTaskId={editTargetTaskId}
editTask={editTask}
/>
</li>
))}
</ul>
)
And this is the child component list item.
interface ITaskListItemProps {
task: Task;
isEdit: boolean;
editTaskId: string;
list: Task[];
triggerEdit: (e: React.MouseEvent<HTMLLabelElement>) => void;
editTask: (
newInput: string,
list: Task[],
editTaskId: string,
editMode: boolean
) => void;
}
export default function TaskListItem({
isEdit,
editTaskId,
task,
list,
triggerEdit,
editTask,
}: ITaskListItemProps) {
const [updateInput, setUpdateInput] = useState<string>(task.title);
const handleUpdateInput = (e: React.ChangeEvent<HTMLInputElement>) => {
setUpdateInput(e.target.value);
};
if (isEdit && editTaskId === task.id) {
return (
<>
<input type="text" value={updateInput} onChange={handleUpdateInput} />
<button
type="button"
onClick={() => editTask(updateInput, list, editTaskId, isEdit)}
>
update
</button>
</>
);
} else {
return (
<>
<input type="checkbox" />
<label onDoubleClick={triggerEdit}>{updateInput}</label>
</>
);
}
}
It seems to be working well at the first time, but I've got a warning message on the console 'A component is changing an uncontrolled input to be controlled.'.
How can I handle this warning? Should I use some of the other hooks like useEffect()?
Thanks in advance!
CodePudding user response:
If you notice the in your if-else statement, input text
and input checkbox
, the input text
is controlled by a local state, while input checkbox
is not.
This is likely the cause of the error.