1

Good afternoon,

I am trying to display data that is provided to my application by an instance of MongoDB before the initial render. I have yet been successful in doing so either running into errors or warnings.

This is the part of the render method I am working with.

        <div className="right-column">
            <div className="pipeline">

              {this.props.tournamentList.map((x,i) => {
                return <li key={i}>{x.name}</li>
              })}

            </div>

        </div>

this.props.tournamentList has a value of an array of objects like so:

tournamentList:
Array[15]
0:
{…}
1:
{…}
2:
{…} ...

This list comes to my application through the componentWillMount lifecycle method, so before the initial render. To me I should be able to iterate through the array and make a dynamically generated list of tournaments provided by my database.

Yet with the code I provided I am getting this warning:

Uncaught (in promise) Error: Objects are not valid as a React child (found: object with keys {prelims, outRounds, notes}). If you meant to render a collection of children, use an array instead or wrap the object using createFragment(object) from the React add-ons. Check the render method ofBuildTournament.

I tried this approach, creating called displayTournaments and calling it inside the div with the class of "pipeline" but nothing happens, no errors no render:

displayTournaments(){

        const { tournamentList } = this.props;

        tournamentList.map((x,i) => {
                    return <li key={i}>{x.name}</li>
                  })


    }

Clearly I am doing something wrong but I don't know what. Is this an instance where I should be using keyed fragments as suggested by the error message? Would anyone smarter than myself be willing to lend some insight?

Cheers.

2
  • Performing an async action in the componentWillMount lifecycle hook does not ensure the data is available on the initial render. You will need to make the call and then update your state to trigger a re-render. Your component will need a no-data render state as well. Commented Jul 22, 2017 at 23:21
  • Default props of tournamentList is just an empty array right? In your case in the initial store. Commented Jul 22, 2017 at 23:21

3 Answers 3

2

Update: Sorry, I misunderstood your question. Kyle is correct with the loading state.

In addition, using a library like lodash will allow you to map over objects in a more natural manner. The native javascript map method doesn't handle objects all that well.

https://www.npmjs.com/package/lodash

you use it much the same way. just

import _ from lodash

then

_.map(objectToMap, (x) => <Component key={x}>{x.thing}</Component>)
Sign up to request clarification or add additional context in comments.

5 Comments

He's using map on an array of objects. There is no need for lodash to do this.
He doesn't Need to use something like lodash but it makes working with the actual objects in the array a bit more natural and intuitive. You're right though.
Update your answer to reflect that it is optional and might benefit him in some way, and I will remove my down vote.
oops. I misunderstood the initial question. Sorry bout that.
Thanks for getting back to me, I will have to check out lodash and see how it works for my own learning. Another answer suggested I use Object.keys and I may use that since it's a "vanilla" solution- not needing another NPM package. Thoughts?
1

Here would be a simple solution that would have a loading state, error state, and success state.

The first thing to note is you will need to use Object.keys() to your object in order to map over the array of keys since you cannot map plain objects. You should also note that the map will return the key of each object so in order to target key values pairs you will need to use a syntax like this tournaments[key].name rather than just doing tournament.name as you are targeting an object with in an object and then grabbing the value.

Let me know if you need any more help with this

import React from 'react'
import Loader from '../Loader'

const resultList = ({ tournaments, isFetching = true }) => (
<div>
    {
        isFetching
            ?   <div>
                    <Loader /><br />
                    <span>Loading&hellip;</span>
                </div>
            :   <div>
                    {
                        Object.keys(tournaments).length
                        ?   <div>
                                {
                                  tournaments.map((key) => (
                                        <section id={tournaments[key].id} key={key}>
                                            <p>{tournaments[key].name}</p>
                                        </section>
                                    ))
                                }
                            </div>
                        :   <div>
                                <p>There are no tournaments....</p>
                            </div>
                    }
                </div>
    }
</div>
);

export default resultList

1 Comment

I am going to try and use your solution first because Object.keys is familiar, but I am confused when I see the arrangement of your ternary operations and whatever Loader component you have there. Are they meant to handle the asynchronous loading of data?
1

You are going to need to have a loading state if you get your data in the componentWillMount or componentDidMount lifecycle hooks. The below example will illustrate how this is done.

class ComponentThatGetsAsyncData extends PureComponent {
    constructor( props ) {
        super( props );

        this.state = {
            tournamentList: [ ]
        }
    }

    componentDidMount() {
        // use any http library you choose
        axios.get( "/some_url" )
            .then( ( res ) => {
                // this will trigger a re-render
                this.setState({
                    tournamentList: res.data
                });
            })
            .catch( ( err ) => {
                // handle errors
            });
    }

    render() {
        const { tournamentList } = this.state;
        // i'd use something other than index for key

        // on your initial render your async data will not be available
        // so you give a loading indicator and when your http call
        // completes it will update state, triggering a re-render
        return (
            {
                tournamentList ?
                    tournamentList.map((x,i) => {
                        return <li key={i}>{x.name}</li>
                    }) :
                    <div className="loading">Loading...</div>
            }
        );
    }
}

6 Comments

I like this answer because you are taking into account the async loading of my data and I suspect that the lifecycle hook may be messing with my function. I was under the impression that ComponentWillMount would have avoided this but I suppose not. Does this matter if I'm using Redux? Also what would you use if not index for the key?
@m00saca I personally do my data loading calls in the componentDidMount life-cycle hook because it ensures that the component mounted properly. For the key you can use any unique value. The problem with using the index is that the values will always be the same, but the items may change. This will mess with React's internal reconciliation mechanism. As far as redux goes, the keys will still be important, and the lifecycles methods will still perform the same functions.
@m00saca as far as redux goes... You would do basically the same thing, only when you get your data in your .then method of the http call you would dispatch an action to update your store, which would then trigger a re-render in your component if it is connected using connect from react-redux package and you had provided a mapDispatchToProps function. You can read more about this here: github.com/reactjs/react-redux/blob/master/docs/api.md
Best of luck! If it doesn't work and you need some pointers on redux, just let me know.
@m00saca In addition to my above comment, I highly recommend Getting Started with Redux by Dan Abramov, the creator of redux.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.