-1

To test my understanding of the map() I "fetch"'ed data from the Starwars api url: 'https://swapi.co/api/people' and assigned it to the const 'data'

Since I was only interested in mapping the characters found in results[] I created a const char pointing to that array ie. const char = data.results. When I tried to map the array I got the error "cannot read property of 'map' undefined. I carried out some confidence checks (console.logs) to confirm that the array char existed before attempting to perform the mapping function on it. It does ...so I'm not sure why the following doesn't work char.map((ele) => )

I also tried the mapping with a what I believe to be an exact duplicate of the results array and the mapping worked fine. So clearly there is something I am missing. Any help would be greatly appreciated

// Get data from Web based on Selection const [ data, isloading ] = useFetch( 'https://swapi.co/api/people');

// test to see if we received from useFetch
console.log('Here is the loaded Data', data);

// Just want to map the characters in the Results array so point chars  to the results array in data
const chars = data.results;
console.log(`Check that chars and data.results are equal", ${data.results === chars}`)

// Now Show the Characters
console.log('Here are the Characters from Website after pointing to data.results', chars);
console.log('Here are the Characters using the static array', charsStatic)


const characterList = chars.map((ele) => 
        <Card key={ele.name} name={ele.name} height={ele.height} hair={ele.hair_color} skin={ele.skin_color} vehicle={ele.vehicles} />)

return (
    <div>
        <h1>List of Starwar Characters</h1>
        <div className='container'>
        {isloading ? "Waiting for Data" : characterList}
        </div>
    </div>
);

}

I expected to be able to map the chars array since I could clearly see it when I logged the chars to console. Instead I received the "cannot read property map of undefined" and yet, to me, the char array is defined ;(

Emile Bergeron
  • 17,074
  • 5
  • 83
  • 129
Clipper
  • 1
  • 2
  • Can you please post the results of the `console.log`s? – Michele Jul 04 '19 at 14:52
  • The function is called multiple times. I bet you see the data correctly after it has received it from the server, but the error is thrown before that when it tries to render it for the first time. – JJJ Jul 04 '19 at 14:54
  • Here is the results of console.log...Here are the Characters from Website after pointing to data.results (10) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}] – Clipper Jul 04 '19 at 14:58
  • Possible duplicate of [Can't access object property, even though it exists. Returns undefined](https://stackoverflow.com/questions/17546953/cant-access-object-property-even-though-it-exists-returns-undefined) – Emile Bergeron Jul 04 '19 at 15:02
  • jjj, I have similar suspicions, I am using a user defined fetch hook to fetch the data, which returns two variables isloading , data. I my showListSample function I only render once loading is set to false. – Clipper Jul 04 '19 at 15:04
  • (10) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}] 0: {name: "Luke Skywalker", height: "172", mass: "77", hair_color: "blond", skin_color: "fair", …} not enough space to include the other objects – Clipper Jul 04 '19 at 15:12
  • Emile, I followed suggestion of using a conditional statement to determine if the object exists before trying to map. Please see Cal's suggestion below. – Clipper Jul 04 '19 at 15:53

1 Answers1

1

I suspect, based on the error, that you are getting caught by a problem with async data. Console.log will show the data after it finishes loading, even if it wasn't finished loading yet when the line of code was called (on chrome, you will see a little i with the tooltip letting you know that the value was just computed).

and checking that data.results === chars will evaluate to true either way, as undefined === undefined is true.

Try this:

if (!isLoading) {
const characterList = chars.map((ele) => 
        <Card key={ele.name} name={ele.name} height={ele.height} hair={ele.hair_color} skin={ele.skin_color} vehicle={ele.vehicles} />)

return (
    <div>
        <h1>List of Starwar Characters</h1>
        <div className='container'>
        {isloading ? "Waiting for Data" : characterList}
        </div>
    </div>
);
}
else
{
    return (
        <div>
            <h1>List of Starwar Characters</h1>
            <div className='container'>
            Waiting for Data
            </div>
        </div>
}

This is not an elegant solution, but should work if the problem is indeed about async. The key is to not call .map until isLoading is false.

Cal Irvine
  • 1,104
  • 8
  • 17
  • Hi Cal, Thanks for the suggestion! I tried the above conditional with the same results. "property of map..... In my user defined useFetch hook I am using fetch with async. Since async returns a promise I call another function from useEffect() – Clipper Jul 04 '19 at 15:33
  • here is the hook – Clipper Jul 04 '19 at 15:33
  • export default function useFetch(url) { const [data, setData] = useState([]);a const [isloading, setIsLoading] = useState(false) async function getData() { setIsLoading(true) const response = await fetch(url); const data = await response.json(); setIsLoading(false); setData(data); } useEffect(() => { getData(); }, []); return [data, isloading]; } – Clipper Jul 04 '19 at 15:35
  • Try changing the if(isLoading) to if(char), see if it ever becomes defined. – Cal Irvine Jul 04 '19 at 15:35
  • Sorry the above isn't formatted properly. I'm new to stackoverflow, but I will find out how to display the sample code in a more readable form – Clipper Jul 04 '19 at 15:37
  • Cal, I followed your suggestion. The component initially rendered as as expected ie "Waiting for Data" However, as soon as chars array became available I received the property of map underfined ??? – Clipper Jul 04 '19 at 15:45
  • @Clipper That means that your data is not structured the way you think it is. If char is defined AND char is an array, you will not have this problem. If char is anything else (an object, or any other data type) you cannot use .map. – Cal Irvine Jul 04 '19 at 15:49
  • I console.log the following after the if(chars) condition console.log('**********Chars after conditional statement************', chars); – Clipper Jul 04 '19 at 16:00
  • sample **********Chars after conditional statement************ (10) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}] 0: {name: "Luke Skywalker", height: "172", mass: "77", hair_color: "blond", skin_color: "fair", …} __proto__: Array(0) – Clipper Jul 04 '19 at 16:03
  • I could only pass the first object in the array due to the character limitation of the comment block. Can you see anything wrong with the data structure? What am I missing? – Clipper Jul 04 '19 at 16:07
  • See how it says Array(0)? That means the data isn't populated yet when the console.log is called. Try logging a copy of the data console.log([...chars]); – Cal Irvine Jul 04 '19 at 16:09
  • It might be a case of me not being able to see the "Forest" for the "trees". :) – Clipper Jul 04 '19 at 16:09
  • array(0) is the first object in the array, just above you will notice that the fully returned array(10) shows 10 objects. – Clipper Jul 04 '19 at 16:11
  • I just showed the first object in the array due to character space limitations – Clipper Jul 04 '19 at 16:13
  • As a test, try setting chars to static data: const chars = [{...whatever goes in here}]; Put in a couple characters worth of info. If it works, it's an async problem that needs diagnosing, if it doesn't... Javascript is broken. – Cal Irvine Jul 04 '19 at 16:23
  • Cal, I just rechecked my code with your suggestion to only render once the object was received. Although the if(! isloading) did not work the if(chars) did! So thank you very much for staying with me.!!!! I probably made a typo when trying your suggestions initially. How do I ensure that you get credit for your solution? – Clipper Jul 04 '19 at 16:30