Sorry for the title not being sufficient, it's a bit hard for me to sum it up into a title.
So, I have a simple Todo app, consisting of 5 components and 2 hooks.
When I am editing a task, I call the 'editTodoForm' component (a more suitable and clear name would be updateTodoForm, sorry for that), in which I am handling the request for submitting the edit.
When the user presses 'Enter' to submit, I enter a function 'handleSubmitAndToggle' which is supposed to toggle my boolean 'isEditing', which is responsible for either showing a task in its 'normal' appearance, or in its 'editTodoForm' appearance. Once done and submitted, the function for toggling the 'isEditing' boolean variable is doing its simple job, and for some reason, in return, I get the following errors:
1)
Uncaught Error: Objects are not valid as a React child (found: object with keys {id, txt, completed}). If you meant to render a collection of children, use an array instead.
react_devtools_backend.js:4026 The above error occurred in the
component:
at p at span at http://localhost:3000/static/js/bundle.js:1718:66 at Typography (http://localhost:3000/static/js/bundle.js:18021:87) at div at http://localhost:3000/static/js/bundle.js:1718:66 at ListItemText (http://localhost:3000/static/js/bundle.js:12299:82) at div at http://localhost:3000/static/js/bundle.js:1718:66 at Grid (http://localhost:3000/static/js/bundle.js:9603:87) at WEBPACK_DEFAULT_EXPORT (http://localhost:3000/static/js/bundle.js:177:5) at span at http://localhost:3000/static/js/bundle.js:1718:66 at Typography (http://localhost:3000/static/js/bundle.js:18021:87) at div at http://localhost:3000/static/js/bundle.js:1718:66 at ListItemText (http://localhost:3000/static/js/bundle.js:12299:82) at li at http://localhost:3000/static/js/bundle.js:1718:66 at ListItem (http://localhost:3000/static/js/bundle.js:12630:83) at ul at http://localhost:3000/static/js/bundle.js:1718:66 at List (http://localhost:3000/static/js/bundle.js:13018:82) at div at http://localhost:3000/static/js/bundle.js:1718:66 at Paper (http://localhost:3000/static/js/bundle.js:15164:82) at div at WEBPACK_DEFAULT_EXPORT (http://localhost:3000/static/js/bundle.js:662:5) at div at http://localhost:3000/static/js/bundle.js:1718:66 at Grid (http://localhost:3000/static/js/bundle.js:9603:87) at div at http://localhost:3000/static/js/bundle.js:1718:66 at Grid (http://localhost:3000/static/js/bundle.js:9603:87) at div at http://localhost:3000/static/js/bundle.js:1718:66 at Paper (http://localhost:3000/static/js/bundle.js:15164:82) at div at WEBPACK_DEFAULT_EXPORT (http://localhost:3000/static/js/bundle.js:367:78) at div at App
The weird part is, that when I do console.log() within the toggling function, it seems to finish all the lines within its scope, so for some reason, as it attempts to return to the 'Todo' component, it fails.
I will emphasize that in the beginning (starting with that 'screen') the same Component it fails to return to works just fine.
TodoApp.js
import React, {useState} from "react";
import TodoList from './TodoList';
import Paper from '@mui/material/Paper';
import List from '@mui/material/List';
import TextField from '@mui/material/TextField';
import ListItem from '@mui/material/ListItem';
import Divider from '@mui/material/Divider';
import ListItemText from '@mui/material/ListItemText';
import { AppBar, Toolbar, Typography } from "@mui/material";
import { fontWeight } from "@mui/system";
import TodoForm from "./TodoForm";
import Grid from '@mui/material/Grid';
import {v4} from 'uuid';
export default function () {
let tasks = [
{id: v4(), txt: "thisisTask1", completed: true},
{id: v4(), txt: "thisisITask2", completed: false},
{id: v4(), txt: "SO ORIGINAL", completed: false}
]
let [tasksVal, tasksEdit] = useState(tasks);
let handleDelete = (id2filter) => {
let res = tasksVal.filter(task => task.id !== id2filter);
tasksEdit(res);
}
let handleToggleCompletion = (todoId)=>{
let newTasks = tasksVal.map(task =>
task.id===todoId ? {id: task.id, txt: task.txt, completed: (!task.completed)} : task
)
tasksEdit(newTasks);
}
let handleEditTask = (id, taskTxt) => {
let updatedTask = tasksVal.map(task => task.id === id ? {...task, txt: taskTxt} : task)
tasksEdit(updatedTask);
}
console.log()
//issue seems to lay here - if I change txt: "texthere" - no errors (but doesn't add the task either)
let addToDo = (taskTxt) => { // a function to later on send to TodoForm for when a Submit occures.
tasksEdit([...tasksVal, { id: v4(), txt: taskTxt, completed: false }])
};
return(
<div>
<Paper style={{padding: 0, margin: 0, height: "100vh", backgroundColor: "whitesmoke"}}>
<AppBar position='static' style={{height: "64px"}} color="primary">
<Toolbar>
<Typography style={{
letterSpacing: "3px",
fontSize: "40px"}}>
Todos with Hooks!
</Typography>
</Toolbar>
</AppBar>
<Grid container justifyContent="center" style={{marginTop: "1rem"}}>
<Grid item xs={12} md={8} lg={6}>
<TodoList
tasks={tasksVal}
handleDelete={handleDelete}
toggleCompletion={handleToggleCompletion}
handleEditTask={handleEditTask}
/>
<TodoForm AddToDo={addToDo}/>
</Grid>
</Grid>
</Paper>
</div>
)
}
TodoList.js
import React from "react";
import Paper from '@mui/material/Paper';
import List from '@mui/material/List';
import TextField from '@mui/material/TextField';
import ListItem from '@mui/material/ListItem';
import ListItemText from '@mui/material/ListItemText';
import Todo from "./Todo";
import { Divider } from "@mui/material";
export default function ({tasks, handleDelete, toggleCompletion, handleEditTask}) {
let toDos = tasks.map((task,i) =>
<React.Fragment key={i}>
<ListItem alignItems="flex-start">
<ListItemText>
<Todo
taskItself={task}
handleDelete={handleDelete}
handleToggleCompletion={toggleCompletion}
handleEditTask={handleEditTask}/>
</ListItemText>
</ListItem>
<Divider/>
</React.Fragment>
)
return(
<div>
<Paper>
<List>
{toDos}
</List>
</Paper>
</div>
)
}
Todo.js
import React from "react";
import { red } from "@mui/material/colors"
import Checkbox from '@mui/material/Checkbox';
import Grid from '@mui/material/Grid';
import EditIcon from '@mui/icons-material/Edit';
import DeleteIcon from '@mui/icons-material/Delete';
// import ListItemSecondaryAction from '@mui/icons-material/ListItemSecondaryAction';
import { IconButton, ListItem, ListItemText, ListItemSecondaryAction, TextField, Paper } from "@mui/material";
import useToggleState from "./hooks/useToggleState";
import TodoForm from "./TodoForm";
import EditTodoForm from "./editTodoForm";
export default function ({taskItself, handleDelete, handleToggleCompletion, handleEditTask}){
let task = <p style={{letterSpacing: "3px"}}>{taskItself.txt}</p>
let handleDeleteToDo = () => {
handleDelete(taskItself.id)
}
let toggleCompletion = () => {
handleToggleCompletion(taskItself.id)
}
let [isEditing, toggleIsEditing] = useToggleState()
return (
<Grid container>
{(!isEditing) ? (<>
<Checkbox onClick={toggleCompletion} style={{marginRight: 0}} checked={taskItself.completed}/>
{taskItself.completed ?
<ListItemText style={{color: "red", letterSpacing: "3px", textDecoration:"line-through"}}>{task}</ListItemText> :
<ListItemText style={{color: "green", letterSpacing:"3px"}}>{task}</ListItemText>
}
<IconButton aria-label="edit" onClick={toggleIsEditing}>
<EditIcon/>
</IconButton>
<IconButton aria-label="delete" onClick={handleDeleteToDo}>
<DeleteIcon/>
</IconButton>
</> )
:<>
<EditTodoForm task={taskItself} toggleEditForm={toggleIsEditing} handleEditTask={handleEditTask}/>
</>
}
</Grid>
)
}
TodoForm.js
import React from "react";
import useInputStatee from "./hooks/useInputStatee";
import { Paper, TextField } from "@mui/material";
export default function ({AddToDo}) {
const [inputVal, inputChange, inputReset] = useInputStatee("");
return (
<div>
<Paper style={{margin: "1rem 0", padding: "0rem 1" }}>
<form onSubmit={e => {
e.preventDefault();
AddToDo(inputVal);
inputReset();
}}>
<TextField fullWidth label="add new to do" margin="normal" value={inputVal} onChange={inputChange}/>
</form>
</Paper>
</div>
)
}
editTodoForm.js
import { TextField } from "@mui/material";
import React from "react";
import useInputStatee from "./hooks/useInputStatee";
export default function ({task, handleEditTask, toggleEditForm}) {
const [value, handleChange, reset] = useInputStatee(task.txt)
let handleSubmitAndToggle = (e) => {{
e.preventDefault();
handleEditTask(task.id, task);
reset();
toggleEditForm();
console.log("SRFDWSFLDS#@$@#$@$$#???")
}}
return (
<form onSubmit={handleSubmitAndToggle}>
<TextField fullWidth value={value} onChange={handleChange} margin="normal"/>
</form>
)
}
useToggleState.js
import React, {useState} from "react";
export default function (boolVar = false) {
let [state, setState] = useState(boolVar);
let toggleState = () => {
setState(!state);
}
return [state, toggleState];
}
useInputStatee.js
import React, {useState} from "react";
export default function (initVal) {
let [value, setValue] = useState(initVal);
let handleChange = (e) => { // when we get from a form ... we get an e.target.value
setValue(e.target.value);
}
let reset = () => { setValue(""); }
return [value, handleChange, reset];
}
Thanks !