Chris Morris
Chris Morris

Reputation: 983

Mapping and returning JSX being treated as an object

I have an object that contains other objects. This is just to keep an ID against each entry for easy referencing.

My posts data structure is as follows

{
    1: {
        id: 1,
        title: "test"
    },
    2: {
        id: 2,
        title: "qwerty"
    },
    3: {
        id: 3,
        title: "asdf"
    },
}

I'm using lodash to loop the items as an array and return an array of JSX elements. However the return is being treated as an object.

postsList() {
    return _.map(this.props.posts, post => {
        return (
            <a key={ post.id }>{ post.title }</a>
        );
    });
}

This happens no matter how I handle the transformation. I can convert the top level object to an array first then map it but I get the same results. The result is always an object. If i console log the post lists it looks as follows

(3) [{...}, {...}, {...}]
0: {$$typeof: Symbol(react.element), type: "a", key: "1", ref: null, props: {…}, …}
1: {$$typeof: Symbol(react.element), type: "a", key: "2", ref: null, props: {…}, …}
2: {$$typeof: Symbol(react.element), type: "a", key: "3", ref: null, props: {…}, …}
length: 3
__proto__: Array(0)

And when I try and use the function in my component with {this.postsList()} I get the error Objects are not valid as a React child (found: object with keys {rendered}). If you meant to render a collection of children, use an array instead.

Upvotes: 1

Views: 2938

Answers (2)

tombraider
tombraider

Reputation: 1167

The Lodash map method returns an array, so by returning the map, you're actually returning an array of objects, hence why you're getting the error above. Also, drop the Lodash map, it's not needed.

Take a look at this example: https://jsfiddle.net/4x63fn9z/

const data = {
    1: {
        id: 1,
        title: "test"
    },
    2: {
        id: 2,
        title: "qwerty"
    },
    3: {
        id: 3,
        title: "asdf"
    },
}

class MyComponent extends React.Component {
    render() {
        return (
            <div>
                {Object.values(data).map(item => (
                    <div>{item.id}</div>
                 ))}
            </div>
        )
    }
}

ReactDOM.render(
  <MyComponent />,
  document.getElementById('container')
);

After fixing your data (you had duplicate keys), we can use Object.values() to give us an array of objects rather than relying upon Lodash. From there, we can loop over that array using the native .map() method and return the appropriate JSX.

Upvotes: 1

Treycos
Treycos

Reputation: 7492

Your JSON object is incorrect. You are redefining the key 1 multiple times, the only value that is contained at the end is the last one : { id: 3, title: "asdf" }

const posts = {
    1: {
        id: 1,
        title: "test"
    },
    1: {
        id: 2,
        title: "qwerty"
    },
    1: {
        id: 3,
        title: "asdf"
    },
}

console.log(posts)

You have 2 solutions.

The easiest solution to your problem would be to convert your object into the following array and map over it :

Working example :

const posts = [
    {
        id: 1,
        title: "test"
    },
    {
        id: 2,
        title: "qwerty"
    },
    {
        id: 3,
        title: "asdf"
    },
]

const App = props => <div>{posts.map(({ id, title }) => <a key={id}>{title}</a>)}</div>

ReactDOM.render(<App />, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.0/umd/react-dom.production.min.js"></script>
<div id='root'>

Another one would be to give a different key to each element and map their values :

const posts = {
    1: {
        id: 1,
        title: "test"
    },
    2: {
        id: 2,
        title: "qwerty"
    },
    3: {
        id: 3,
        title: "asdf"
    },
}

const App = props => <div>{Object.values(posts).map(({ id, title }) => <a key={id}>{title}</a>)}</div>

ReactDOM.render(<App />, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.5.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.5.2/umd/react-dom.production.min.js"></script>
<div id='root'>

Lodash does not seem to be required in any case

Upvotes: 0

Related Questions