So, I have been attempting to read about this error ( Objects are not valid as a React child. If you meant to render a collection of children, use an array instead and here https://www.g2i.co/blog/understanding-the-objects-are-not-valid-as-a-react-child-error-in-react ), and I (think I) understand it's an issue of passing a 'complex' object to react's child component/passing a JS object to a component. (am I correct?)
I have located where my issue lays in my code, but I cannot really understand why would the variable be interpreted as a complex object in the first place.
The issue occurs when I'm trying to create at TodoApp.js, addToDo() function. This function is later on transferred to a child component 'TodoForm.js', so it could, on handleSubmit, call his father's function addToDo():
In addToDo(), if I edit, where I add a new element, the field txt 's value to a string for example, it works.
TodoApp.js (in code's comments I have pointed out where is exactly the issue)
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";
export default function () {
let tasks = [
{id: 1, txt: "thisisTask1", completed: true},
{id: 2, txt: "thisisITask2", completed: false},
{id: 3, txt: "SO ORIGINAL", completed: false}
]
let [tasksVal, tasksEdit] = useState(tasks);
const tasksTxts = tasksVal.map((task) =>
<><ListItem><ListItemText>{task.txt}</ListItemText></ListItem> <Divider/></>
)
//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: 4, 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>
<TodoList tasksTxts={tasksTxts}/>
<TodoForm AddToDo={addToDo}/>
</Paper>
</div>
)
}
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("");
console.log(inputVal)
return (
<div>
<Paper>
<form onSubmit={e => {
AddToDo(inputVal);
// inputReset();
}}>
<TextField value={inputVal} onChange={inputChange}/>
</form>
<p>{inputVal}</p>
</Paper>
</div>
)
}
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];
}
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';
export default function ({tasksTxts}) {
return(
<div>
<Paper>
<List>
{tasksTxts}
</List>
</Paper>
</div>
)
}
Thanks in advance!
CodePudding user response:
The issue is in tasksTxts
, it's rendering an object instead of valid JSX.
const tasksTxts = tasksVal.map((task) =>
<>
<ListItem>
<ListItemText>
{task.txt} // <-- task.txt is object { taskTxt }
</ListItemText>
</ListItem>
<Divider/>
</>
);
...
let addToDo = (taskTxt) => {
tasksEdit([
...tasksVal,
{
id: 4,
txt: { taskTxt }, // <-- caused here
completed: false
}
])
};
The tasksTxts
needs to access into the additional taskTxt
property:
const tasksTxts = tasksVal.map((task) =>
<>
<ListItem>
<ListItemText>
{task.txt.taskTxt}
</ListItemText>
</ListItem>
<Divider/>
</>
);
Or the task.txt
just needs to be the value you want to display:
let addToDo = (taskTxt) => {
tasksEdit(tasksVal => [
...tasksVal,
{ id: 4, txt: taskTxt, completed: false }
])
};
Unrelated Suggestion
You will want to also add a valid React key to the mapped tasks.
Example:
const tasksTxts = tasksVal.map((task) =>
<React.Fragment key={task.id}> // <-- Valid React key
<ListItem>
<ListItemText>
{task.txt}
</ListItemText>
</ListItem>
<Divider/>
</React.Fragment>
);
CodePudding user response:
You are correct, the error is inside the addToDo()
function:
let addToDo = (taskTxt) => {
tasksEdit([...tasksVal, { id: 4, txt: {taskTxt}, completed: false }])
// ^^^^^^^^^ the error is here
};
taskTxt
is being wrapped inside another object instead of just being put on directly.
So the correct version of this code is:
tasksEdit([...tasksVal, { id: 4, txt: taskTxt, completed: false }])
// ^^^^^^^ note no wrapping braces any more