0

I'm still pretty new with ReactJS and now I ran into something which confuses me totally.

I've created a bunch of components which render a Bootstrap modal. Furthermore I fetch initial state parameters from the sever through the componentDidMount function. My component structure looks something like this:

<App>
  <Modal Lauch Button /> 

  <Modal>
   <Modal Header />
   <Modal Body />     <-- here's the problem
  </Modal>
</App>

and my initial state array is of the form (of course JSON encoded):

array:5 [▼
   ...
   ...
   "private" => true
   "files" => array:2 [▼
       1 => array:7 [▼
          "id" => 0
          "route" => "some route"
          ...
          ...
       ]
       2 => array:7 [▼
          "id" => 1
          "route" => "some other route"
          ...
          ...
       ]
   ]
]

and my (minified) React application like this:

<script type="text/babel">

    var L5fmModalBody = React.createClass({

        render: function() {

            return(
                <Modal.Body>
                    <Grid fluid={true}>
                        <Row>
                            {
                                this.props.files.map(function (file) {
                                    return <L5fmModalBodyFileContent id={file.id} route = {file.route} / >
                                })
                            }
                        </Row>
                    </Grid>
                </Modal.Body>
            );
        }

    });

    var L5fmModalBodyFileContent = React.createClass({

        render : function() {
            return(
                <Col xs={6} md={4} className="img-row">
                    <div className="thumbnail thumbnail-img">
                        <img key={this.props.id} src={this.props.route} />
                    </div>
                </Col>
            );
        }

    });

    var L5fmModal = React.createClass({

        getInitialState : function() {
            return {
                data : []
            };
        },

        componentDidMount: function() {
            $.ajax({
                url: 'L5fm/setInitialState',
                dataType: 'json',
                cache: false,
                success: function(data) {
                    this.setState({data: data});
                    console.log(data);
                    console.log(this.state.data);
                }.bind(this),
                error: function(xhr, status, err) {
                    console.error(this.props.url, status, err.toString());
                }.bind(this)
            });
        },

        render: function() {

            return(

                <Modal {...this.props} bsSize="large" aria-labelledby="contained-modal-title-lg">
                    <Modal.Header closeButton>
                        <div className="header-button-group">
                            some buttons
                        </div>
                    </Modal.Header>

                    <L5fmModalBody files={this.state.data.files} />
                </Modal>
            );
        }

    });


    var App = React.createClass({
        getInitialState: function() {
            return { lgShow: false };
        },
        render: function() {
            let lgClose = () => this.setState({ lgShow: false });

            return (
                 <Button bsStyle="primary" onClick={()=>this.setState({ lgShow: true })}>
                    Launch large demo modal
                 </Button>
                 <L5fmModal show={this.state.lgShow} onHide={lgClose} />
            );
        }
    });

    ReactDOM.render(<App />, document.getElementById("modal"));

</script>

Whenever I run the application I get following error:

L5fmModalBody_renderTypeError: this.props.files.map is not a function. (In 'this.props.files.map', 'this.props.files.map' is undefined)

If I console.log(this.state.data.files) in my <Modal> component, it also shows that this.state.data.files is undefined? Obviously I have not understood what is going on. I've also tried to fetch data with componentWillMount but this did not help either. What am I doing wrong?

Thanks!


UPDATE

I think I got a step further. The actual issue is not that this.set,state is null at the start, because after componentDidMount is called and the "new" states are set, the UI gets rendered again. Furthermore I found out that JSON apparently handles assoziative arrays as objects and I believe that I can't call map on an object.

Any ideas?

3
  • What does this.state.data log? Commented Dec 30, 2015 at 11:21
  • If I put console.log(this.state.data) into the render function of <L5fmModal> I get [] (0) as log output. I guess that is just the predefined empty data array. But how can I fetch data from the server and THEN render the whole application? Commented Dec 30, 2015 at 11:48
  • 1
    @FlorianRagossnig: no need to do like that because first time it will render just UI and then call the API once API success function you have already set state again so it will again call your render function and now you have data so it will populate UI with data Commented Dec 30, 2015 at 12:52

2 Answers 2

2

yes of course it will throw error because you are passing this <L5fmModalBody files={this.state.data.files} /> and your state it's declare in data : [] that means when first render happen data is just a blank array and your component render on your browser once that done then your L5fmModal's componentDidMount called and it will fetch the data from DB and again update state and render function called and data have a file field but what about very first load when your array is just an empty at that time file field is undefined so you have to implement below mentioned code in your L5fmModalBody

     {
(type of this.props.files != 'undefined')? this.props.files.map(function (file) {
                                        return <L5fmModalBodyFileContent id={file.id} route = {file.route} / >
                                    }):null                                
}

or you can also prevent from your L5fmModal it self using

{this.state.data.length>0 ? <L5fmModalBody files={this.state.data.files} />:null}
Sign up to request clarification or add additional context in comments.

4 Comments

Hi Dhaval! thanks for your reply. Unfortunately this solution does not work. The Modal gets rendered but without body content.
@FlorianRagossnig:that means your data is always null check your API call
I thought so and I think I'm a step further now. I realised that my return "array" is actually not an array but an object and I believe I can't use map on an object. Do you have any tips for this issue?
@FlorianRagossnig: yes you can use Object.keys(data).map(function(d){return <L5fmModalBodyFileContent id={data[d].id} />});
1

Thanks to Dhaval Patel I finally have the answer to this issue. The problem was not the null value of the initial state because when componentDidMount gets called the UI gets rendered again and receives the updated states from the server. The actual problem was that JSON treats associative arrays as objects and therefore I could not call a simple map.

I just changed

{
    this.props.files.map(function (file) {
        return <L5fmModalBodyFileContent id={file.id} route = {file.route} / >
    })
}

to

{
    Object.keys(object).map(
        function(d){
            return <L5fmModalBodyFileContent id={object[d].id} route={object[d].route} />
    })
}

where object = this.props.files and it works like a charm!

To avoid the error of the (at first) undefined object I've used

{this.state.data.length!=0 ? <L5fmModalBody files={this.state.data.files} />:null}

in the <Modal /> component.

Comments

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.