0

I have a reactJS application where I am trying to dynamically render some data that I read in with a fetch() promise. This is the code of my application:

    import React from 'react'; 
import '../styles/app.css'; 

//think of react components as functions 
class Testpage2 extends React.Component { 

constructor(props) { 
    super(props); 
    this.state = { 
        numberOfRecords: 0,
        productArray: [{
            barcode: '',
            name: ''
        }]
    }; 

}  

componentDidMount() { 

    let currentComponent = this; 
    var recordCount = 0;
    var tempData = [];

    //Make use of the API not the web service. 
    let url = "http://wmjwwebapi-dev.us-west-2.elasticbeanstalk.com/api/getdata"; 
    const options = { method: 'GET' }; 

    fetch(url, options) 
    .then(function(response) { 
        return response.json(); 
    }) 
    .then(function(myJson) { 
        if (myJson == undefined) 
        { 
        console.log("fetch failed"); 
        } 
    else 
    { 
        //inspect the data that the WebAPI returned 
        var return_code = myJson[0].return_code; 
        if (return_code == "Default Return code"){ 
            recordCount = -2; 
        } else { 
            tempData = JSON.parse(myJson[0].return_string);
            recordCount = tempData.barcode.length; 
        }
        currentComponent.setState(
        {
            numberOfRecords: recordCount,
            productArray: currentComponent.state.productArray.push(
            { 
                name: tempData.name, 
                barcode: tempData.barcode 
            })
        }
    );
    } 
    }); 
} 

render() { 
    console.log(this.state.productArray);
    return ( 
        <div> 
            { this.state.productArray.map((prod, index) => <li key={index}>{prod.barcode}</li>)}
        </div> 
    ) 
} 
} 

export default Testpage2

and this is the error message that I am getting:

Uncaught (in promise) TypeError: this.state.productArray.map is not a function
at Testpage2.render (testpage2.js:67)

This is the result of the console.log() that I added in the render() function:

enter image description here

I'm not really sure what this error is telling me or how to go about debugging the issue.

Any help is greatly appreciated. Thank you.

Jonathan Small
  • 1,027
  • 3
  • 18
  • 40
  • Can you tell me what the result is when you console.log "this.state.productArray" in the render function? – Patrick Hollweck Aug 26 '18 at 22:37
  • I edited the post to include an image from the console.log() – Jonathan Small Aug 26 '18 at 22:48
  • Thanks but I already answered your question... Can you confirm that it works then? – Patrick Hollweck Aug 26 '18 at 22:50
  • Works perfectly! Thank you so much. – Jonathan Small Aug 26 '18 at 22:54
  • Can you check that `Array.isArray(this.state.productArray)` is `true`? – Dan Aug 26 '18 at 22:57
  • I updated my answer, to follow a best practice... You may want to incorporate this update into your code as well @JonathanSmall – Patrick Hollweck Aug 26 '18 at 22:57
  • Possible duplicate of [React .map is not a function](https://stackoverflow.com/questions/44574367/react-map-is-not-a-function) – Hemadri Dasari Aug 26 '18 at 23:10
  • I jumped to my conclusion to quickly. While the error message is no longer being generated, only 1 instance is being rendered.If you look at the image in the post, you will see that there are values in this.state.productArray.barcode[0], [1], and [2] but the only value that gets rendered when I execute the code is this.state.productArray.barcode[2]. Why wouldnt the map function execute on each element of the this.state.productArray array? – Jonathan Small Aug 26 '18 at 23:12
  • There are so many questions raised with same problem in stackoverflow and obviously every question has n no of answers provided. Please do research about these problems in the internet. Please mark these questions as duplicate rather than answering. – Hemadri Dasari Aug 26 '18 at 23:13
  • Jonathan you are directly mapping data in render you need to do conditional check in render like { this.state.productArray && this.state.productArray!= undefined && this.state.productArray.map. Please have a look at stack overflow Solutions. Plenty of Solutions out there for this issue – Hemadri Dasari Aug 26 '18 at 23:17
  • @Think-Twice I'm actually in the process of changing the map to call a function to render as opposed to rendering inline. – Jonathan Small Aug 26 '18 at 23:21

4 Answers4

1

The return type of array.push is the new length of the array aka a number

So you set the state property productArray to a number and then try to call number.map which is not defined

How to fix?

push first and then use that array to set the state

const updatedArray = [...currentComponent.state.productArray] 
updatedArray.push({ name: tempData.name, barcode: tempData.barcode })

currentComponent.setState({
   numberOfRecords: recordCount,
   productArray: updatedArray
}

Resources: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push

Patrick Hollweck
  • 5,604
  • 2
  • 19
  • 34
0

According to MDN:

The push() method adds one or more elements to the end of an array and returns the new length of the array.

It appears that your code expects that Array.push() will return the modified array itself:

productArray: currentComponent.state.productArray.push(...

To prevent the state corruption you should do construct the new array separately, before invoking setState().

kristaps
  • 1,705
  • 11
  • 15
0

Array's push() function returns integer, so you cannot call map() function on it. Try to change your function to:

currentComponent.setState({
  numberOfRecords: recordCount,
  productArray: [...currentComponent.state.productArray, { 
    name: tempData.name, 
    barcode: tempData.barcode 
  }]
})
Andriy
  • 14,781
  • 4
  • 46
  • 50
0

The JavaScript Array.push method does not return the modified array, it returns the new length of the array, which is a number. Numbers in JavaScript do not have the map method.

You need to do first create a clone of the productArray, then push the new data, and finally set state:

const newProductArray = [...currentComponent.state.productArray]
newProductArray.push({ 
    name: tempData.name, 
    barcode: tempData.barcode 
})
currentComponent.setState(
    {
        numberOfRecords: recordCount,
        productArray: newProductArray
    }
)

See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push

inostia
  • 7,777
  • 3
  • 30
  • 33
  • Still better way of push elements to an array in React is currentComponent.setState(prevState =>({productArray: [...prevState.productArray: { name: tempData.name, barcode: tempData.barcode }]})); – Hemadri Dasari Aug 26 '18 at 23:32
  • Do you have a source for that syntax @Think-Twice? I've never seen `:` used in an array, as far as I know it's reserved for objects. – inostia Aug 26 '18 at 23:48
  • Sorry it’s typo mistake it should be comma not colon – Hemadri Dasari Aug 27 '18 at 00:07