Home > Net >  Cannot read the array property. Objects are not valid as a React child
Cannot read the array property. Objects are not valid as a React child

Time:09-17

I am following a tutorial exercise and I got the following error

Objects are not valid as a React child

I know this error is related to the object as I am trying to access the object but it needs an individual item of an object but not sure.

Why cannot the map loop over each item in the array?

Following is my code

var template = <h1>Indecision App</h1>;
var app = {
    title: 'Indecision App',
    subtitle: 'yo',
    options: []
}
let count = 0;
function checkSubtitles (subtitle){
    if(subtitle){
        return <p>{subtitle}</p>
    }else{
        return undefined
    }
}
function reset(){
    count = 0;
    reRenderApp();
}
function increaseCount(){
    count  ;
    reRenderApp();
}
function onSubmitHandle(e){
    e.preventDefault();
    const options = e.target.elements.options;
    app.options.push(options);
    reRenderApp();
    e.target.elements.options.value = ''
}
function removeAll(){
    app.options = [];
    reRenderApp();
}
function reRenderApp(){
    var templateTwo = (
        <div>
            <h1>{app.title}</h1>
            {checkSubtitles(app.subtitle)}
            <p>Count: {count}</p>
            <p>Array Length: {app.options.length > 0 ? app.options.length : '0 Items'}</p>
            <ol>
                {app.options.map((item)=>{
                    return <li key={item}>{item}</li>
                })}
            </ol>
            <hr></hr>
            <form onSubmit={onSubmitHandle}>
                <input type="text" name="options" />
                <input type="submit" value="Push to the Array" />
                <input type="reset" value="Empty my list" onClick={removeAll} />
            </form>
            <button onClick={()=>{
                increaseCount();
            }}>Increase Count</button>
            <button onClick={()=>{
               reset();
            }}>Reset Count</button>
        </div>
    )
    ReactDOM.render(templateTwo, appRoot)
}
var appRoot = document.getElementById('app');
reRenderApp();
<body>
    <div id="app"></div>
    <script src="https://unpkg.com/[email protected]/umd/react.development.js"></script>
    <script src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script>
    <script src="./app.js"></script>
</body>
</html>

CodePudding user response:

The main problem is, as you mentioned: Objects are not valid as a React child

But, what is happening?

If we go into:

function onSubmitHandle(e){
  e.preventDefault();
  // Line 1
  const options = e.target.elements.options;
  app.options.push(options);
  reRenderApp();
  // Line 2
  e.target.elements.options.value = ''
}

So in Line 1, you're pushing options into the options array.

But, then in Line 2, we can notice options has an attribute (so, it's an object)

So, if you change Line 1, from:

 const options = e.target.elements.options;

To this:

const options = e.target.elements.options.value;

It'd work.

Also, to check what I'm saying you have 2 options:

option 1: console.log

function onSubmitHandle(e){
  e.preventDefault();
  const options = e.target.elements.options;
console.log({ options })
  app.options.push(options);
  reRenderApp();
  e.target.elements.options.value = ''
}

option 2: make that option a valid child of react with JSON.stringify()

<ol>
  {app.options.map((item, index)=>{
    return <li key={index}>{JSON.stringify(item)}</li>
  })}
</ol>

CodePudding user response:

You can do

{app.options.length && app.options.map((item)=>{
                return <li key={item}>{item}</li>
            })}

But you must be sure that "item" here is not an object as you can't render an object

CodePudding user response:

The reason for this is that your options array is going to be filled with elements as you're pushing the input element with the name of "option" into your array - this elements are objects in JS which you can't render out as list items.

  • Use React State to store anything that's going to change in the UI - in this case your list of options> So rather than doing

    var app = { title: 'Indecision App', subtitle: 'yo', options: [] }

    let count = 0;

Do:

const [options, setOptions] = React.useState([]);
const [count, setCount] = React.useState(0);

Title and subtitle are probably not going to change, so just put them in h1 & h2 elements - if they are, then use the state pattern again.

Get rid of the two inputs with types of "submit" & "reset" just use normal button elements instead.

You'll also need an onchange event on your input where the text will go in and each time the onchange event is fired (i.e, when a user types) you'll need to save the input text

const [inputText, setInputText] = React.useState('');

const handleChange = (e) => {
    const {value} = e.target;
    setInputText(value)
}

<input type="text" value={inputText} onChange={handleChange}/>

Then in your onHandleSubmit function, just have

const onHandleSubmit = () => {
    setOptions([...options, inputText]);
    setInputText('')
}

This should work

  • Related