boba poorna
boba poorna

Reputation: 251

React js giving error Objects are not valid as a React child, I used hooks

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

Upvotes: 1

Views: 169

Answers (1)

Drew Reese
Drew Reese

Reputation: 202836

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

Upvotes: 1

Related Questions