0

So here's some code

componentWillMount = () => {
    var URL = 'http://localhost:8000/requests'
    axios({
        url: URL,
        method: 'post', 
        data: {
            message: 'getImages'
        }
    }).then(res => {
        this.setState({currentImage: res.data.picsData[this.props.match.params.imageId]})
    })
}
render() {
    return (
        <div className="image-component-wrapper">
            <img src={'../content/' + this.state.currentImage.fileName} /> 
            <div className="image-meta">
                <p className='image-metadescription'{this.state.currentImage.description</p>
                <p className='image-meta-author'>Uploaded by:
                <i className='image-meta-author-name'>{this.state.currentImage.author}</i></p>
                <div className='image-meta-tags'>
                    Tags:
                    {this.state.currentImage.tags.map(tag => <p>{tag}</p>)}
                </div>
            </div>
        </div>
    )
}

The image displays as it should. The two P tags and the I tag also but I can't get the array of tags to display at all. It gives me Uncaught TypeError: Cannot read property 'map' of undefined

They are contained in the same state object so I don't understand how it's possible. The array is there I'm sure!

redmaster
  • 95
  • 1
  • 12

3 Answers3

1

Allthough sequence of 1. componentWillMount 2. render is correct, your async initialization of this.state.currentImage seems to be the problem no?

It will render before the axios call has finished and actually set the data to state.

A simple fix is to add undef check:

{this.state.currentImage.tags && this.state.currentImage.tags.map(tag => <p>{tag}</p>)}
imiro
  • 167
  • 7
  • 1
    No, because other items in this.state.currentImage like 'description' and 'author' renders fine, and also the image but not the array. They are in the same currentImage they come together from the axios call. Tried your fix, same error. – redmaster Dec 21 '19 at 23:51
  • I should've added the check to currentImage.tags for one thing! But yes, perhaps its an issue in how the tags are actually coming in. Could you debug the object to see how the the tags array looks like? – imiro Dec 21 '19 at 23:57
  • I already debugged that came before the question. Thats why I'm sure the array is there. Here's the output of console.log(res.data.picsData[this.props.match.params.imageId].tags) https://i.ibb.co/G94n1RS/Screenshot-at-2019-12-22-02-02-43.png – redmaster Dec 22 '19 at 00:03
  • Not sure why. Did you try this.state.currentImage.tags &&, maybe theres a extra render were not seeing and this check would allow for rerender with actual data.. – imiro Dec 22 '19 at 00:25
  • Figured it out. The problem was it rendered multiple times but the first time it rendered the state wasn't set yet but since the array.map is a function that gave an error. – redmaster Dec 22 '19 at 00:28
  • Nice job. I also tested it with fiddle, and verified it renders once before the async call is done. This was my first guess too. – imiro Dec 22 '19 at 00:35
1
this.state.currentImage.tags.map(tag => <p>{tag}</p>)

You're trying to access this.state.currentImage.tags.map here but your initial state is defined as

this.state = { currentImage: {} }

Which means that this.state.currentImage.tags is undefined which you can also see in the error message you're getting when you try to call tags.map

Uncaught TypeError: Cannot read property 'map' of undefined

To fix this you would have to make sure that you're defining everything you're using in your state in your constructor like

this.state = {
  currentImage: {
    author: "",
    description: "",
    tags: []
    // etc
  }
}

You might also want to flatten your state here, which is something you could read more about in this question

Xetera
  • 1,390
  • 1
  • 10
  • 17
  • Yes that's how i fixed it actually i defined currentImage as null in state and did an if check in render to skip rendering if still null – redmaster Dec 22 '19 at 01:12
0

@Xetera

import React, { Component } from 'react'
import { connect } from 'react-redux'
import { addPics, setCurrentPic } from '../../redux/actions';
import axios from 'axios';
import { Link } from 'react-router-dom';


class ImageComp extends Component {
    constructor(props) {
        super(props)
        this.state = { currentImage: {}, imageId: this.props.match.params.imageId }
    }
    componentWillMount = () => {
        var URL = 'http://localhost:8000/requests'
        axios({
            url: URL,
            method: 'post',
            data: {
                message: 'getImages'
            }
        }).then(res => {
            this.setState({ currentImage: res.data.picsData[this.props.match.params.imageId] })
        })
    }

    render() {
        return (
            <div className="image-component-wrapper">
                <img src={'../content/' + this.state.currentImage.fileName} />
                <div className="image-meta">
                    <p className='image-meta-description'>{this.state.currentImage.description}</p>
                    <p className='image-meta-author'>Uploaded by:
        <i className='image-meta-author-name'>{this.state.currentImage.author}</i></p>
                    <div className='image-meta-tags'>
                        Tags:
                        {this.state.currentImage.tags.map(tag => <p>{tag}</p>)}
                    </div>
                </div>
            </div>
        )
    }
}
const mapStateToProps = (state) => {
    console.log(state)
    return { pics: state.pics, pic: state.pic }
}


export default connect(mapStateToProps, { addPics, setCurrentPic })(ImageComp)
redmaster
  • 95
  • 1
  • 12