1

I am sending data from Node JS to React JS in array object. In React JS when I am setting response data I am getting error "Objects are not valid as a React child (found: object with keys {eventName, eventDate, merchantid}). If you meant to render a collection of children, use an array instead."

I checked one of the Stackoverflow post useState Array of Objects. I am also setting value same way, but I am getting error.

Below data I am sending from Node JS.

[
  {
    eventName: 'Sankranti',
    eventDate: 2021-01-21T00:00:00.000Z,
    merchantid: 'tp012345'
  },
  {
    eventName: 'Sankranti 1',
    eventDate: 2021-01-26T00:00:00.000Z,
    merchantid: 'tp012345'
  }
]

Below screen shot I can see error and response data on the console. enter image description here

Below my code, I am getting error at setEventList(eventList => [...eventList, response]). Based on comments I added below code.

import React, { useState, useEffect } from 'react';
import { connect } from 'react-redux';
import Carousel from 'react-bootstrap/Carousel'
import axios from 'axios';
import DashboardNavBar from './DashboardNavBar';
import Header from './Header';

const DashboardPage = (props) => {
    const [eventList, setEventList] = useState([])
    const [index, setIndex] = useState()

    if (!props.profileData) {
        useEffect(() => {
            (async () => {
                const eventsList = await axios.get(
                    "http://localhost:3000/api/dashboard"
                );
                    console.log(eventsList.data)
                    const response = eventsList.data
                    setEventList(eventList => [...eventList, response])
                    if(!response){
                        setErrorMsg('Please create Event and then add User !!')
                    }
            })();
        }, []);
    }

    const eventListRender = eventList.length > 0 ?
                        eventList.map((item,index) => {
                            console.log('item name: ', item[index].eventName)
                        return <Carousel.Item>{item[index].eventName}</Carousel.Item>
                        }) :
                        <Carousel.Item>No upcoming events</Carousel.Item>

    const handleSelect = (selectedIndex) => {
        console.log(eventList)
        console.log(selectedIndex)
        setIndex(selectedIndex)
    }

    return (
        <div>
            <DashboardNavBar />
            <Header />
            <p >Welcome !!!!!!</p>
            <Carousel
                activeIndex={index}
                onSelect={handleSelect}
                >
                {eventListRender}
            </Carousel>
        </div>
    );
}

const mapStateToProps = (state) => ({
    profileData: state.auth.profileData
})

export default connect(mapStateToProps) (DashboardPage);

After adding below code it always reading first occurrence

const eventListRender = eventList.length > 0 ?
                        eventList.map((item,index) => {
                            console.log('item name: ', item[index].eventName)
                        return <Carousel.Item>{item[index].eventName}</Carousel.Item>
                        }) :
                        <Carousel.Item>No upcoming events</Carousel.Item>

Please find the updated results

enter image description here

boba poorna
  • 223
  • 4
  • 13
  • This error is happening where you are trying to render your state, can you include a full component code example? – Drew Reese Jan 19 '21 at 05:38
  • one thing that looks wrong is `response` is an array so it should be `setEventList(eventList => [...eventList, ...response])` – buzatto Jan 19 '21 at 05:42
  • Hi Drew Reese, I added code like below, now that error gone, but not reading the objects now(item.eventName = undefined). const eventListRender = eventList.length > 0 ? eventList.map((item) => { console.log('item name: ', item.eventName) return {item.eventName} }) : No upcoming events – boba poorna Jan 19 '21 at 05:56
  • Can you update your question the that new code, SO comments aren't intended for code block display. It's difficult to read code there. – Drew Reese Jan 19 '21 at 06:04
  • Hi Drew Reese, updated my question and added full component. Also added latest result. It always reading first occurrence. – boba poorna Jan 19 '21 at 06:19
  • I don't know what you mean by "reading first occurrence", but `eventList` is the array and the elements are not arrays, so `item[index]` is likely undefined, it seems it should be `item.eventName`. Actually, after another look, is your state an array of arrays? – Drew Reese Jan 19 '21 at 06:37
  • item.eventName was giving Undefined. then i changed to item[index].eventName. Then it started giving the first value 'Sankranti'. When (Carousel React bootstrap) changes for next slide I expected it should read next value 'Sankranti 1', but it is reading same value 'Sankranti'. I will look into any Carousel example in online. – boba poorna Jan 19 '21 at 06:49
  • Can you provide a *running* codesandbox demo that reproduces this issue? You can mock your fetched data. I suspect you either need to map the nested array, or spread your `response` into your root `eventList` array so all the data is a single flat array. If you've only placed one response array into state then `eventList` is an array of length 1, so the second element at `eventList[0][1]` won't be rendered. Can you clarify what your state shape is supposed to be and what the expected output result should be? – Drew Reese Jan 19 '21 at 08:24
  • 1
    This might not be related directly to the error message, but you are calling the useEffect hook from within a conditional which isn't allowed. [Hook Rules](https://reactjs.org/docs/hooks-rules.html). – Ashish Tuteja Jan 19 '21 at 09:48
  • logic added in codesandbox https://codesandbox.io/s/reactbootstrapcarousel-forked-4selu?file=/src/index.js. It is working there, but same code not working if we are populating data from Node js to React js. – boba poorna Jan 19 '21 at 14:30

1 Answers1

1

Issue

Ok, your codesandbox confirms what I suspected. In your sandbox you've directly placed that mock response in your state as a flat array

const [eventList, setEventList] = useState([
  {
    eventName: "Sankranti",
    eventDate: "2021-01-21T00:00:00.000Z",
    merchantid: "tp012345"
  },
  {
    eventName: "Sankranti 1",
    eventDate: "2021-01-26T00:00:00.000Z",
    merchantid: "tp012345"
  }
]);

This allows the render to work as you expected, simply mapping over this flat array of objects.

eventList.map((item, index) => {
  return <Carousel.Item>{item.eventName}</Carousel.Item>;
})

But in your original code you are not updating your state to be a flat array. The response is an array, i.e. [object1, object2] and you append this array to the end of your state's eventList array.

setEventList(eventList => [...eventList, response])

This updates your state to something like this [[object1, object2]], so the mapping function you used only maps one element.

eventList.map((item, index) => {
  return <Carousel.Item>{item[index].eventName}</Carousel.Item>;
})

The reason is because you used the array index of the outer (recall eventList is an array of length 1) to access into the inner nested array (array of length 2). In iterating the outer array the index only reaches value 0, so only the zeroth element of the inner array is rendered.

See a more accurate reproduction of your issue in this code:

const response = [
  {
    eventName: "Sankranti",
    eventDate: "2021-01-21T00:00:00.000Z",
    merchantid: "tp012345"
  },
  {
    eventName: "Sankranti 1",
    eventDate: "2021-01-26T00:00:00.000Z",
    merchantid: "tp012345"
  }
];

function App() {
  const [eventList, setEventList] = useState([]);

  useEffect(() => {
    setEventList((eventList) => [...eventList, response]);
  }, []);

  const eventListRender =
    eventList.length > 0 ? (
      eventList.map((item, index) => {
        return <Carousel.Item>{item[index].eventName}</Carousel.Item>;
      })
    ) : (
      <Carousel.Item>No upcoming events</Carousel.Item>
    );
  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>

      <Carousel>{eventListRender}</Carousel>
    </div>
  );
}

Solution

If the response data is also an array then it seems you should spread it into your eventList state array so it remains a nice, flat array.

Additionally, as pointed out by @Ashish, your useEffect hook usage is invalid and breaks the rules of hooks by being placed in a conditional block. The effect needs to be in the body of the component, so the condition should be tested in the effect callback. Refactor the anonymous async function to be a standard named function, and invoke in a conditional check within the effect callback.

useEffect(() => {
  const getEvents = async () => {
    const eventsList = await axios.get("http://localhost:3000/api/dashboard");
    console.log(eventsList.data);
    const response = eventsList.data;
    setEventList((eventList) => [
      ...eventList, // <-- copy previous state
      ...response, // <-- spread array response
    ]);
    if (!response) {
      setErrorMsg("Please create Event and then add User !!");
    }
  };

  if (!props.profileData) { // <-- check condition for fetching data
    getEvents();
  }
}, []);

const eventListRender =
  eventList.length > 0 ? (
    eventList.map((item, index) => {
      return <Carousel.Item key={index}>{item.eventName}</Carousel.Item>;
    })
  ) : (
    <Carousel.Item>No upcoming events</Carousel.Item>
  );

Demo with mocked axios data fetch.

Edit react-js-giving-error-objects-are-not-valid-as-a-react-child-i-used-hooks

Drew Reese
  • 165,259
  • 14
  • 153
  • 181