-1

I'm having a problem mapping through an array with objects, and I can't find what problem is, but I asume its because of async, but I want you to take a look at it.

I'm getting two error messages and I don't know if they relate:

  1. TypeError: Cannot read property 'map' of null
  2. 1 Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
import {useState, useEffect} from 'react';

// import styled from 'styled-components';

export default function Admin() {
    const [quotes, setQuotes] = useState(null);

    const get_all_quotes = async () => {
        const {data, error} = await supabase
            .from('quotes_en')
            .select('quote')
        console.log(data);
        if (error) console.table(error)
        setQuotes(data)
    }
    useEffect(() => {
        get_all_quotes()
    }, [])

    return (
        <div>
{quotes.map(({id, quote}) => {
                        return <p key={id}>{quote}</p>
                    })
                    }
        </div>
    )
}
Drew Reese
  • 165,259
  • 14
  • 153
  • 181
  • `quotes` initial state is `null`, it can't be mapped. What is `session`? – Drew Reese Jul 18 '21 at 07:02
  • Yes session is to check if person is logged in but that doesn't matter, I removed it thanks –  Jul 18 '21 at 07:05
  • @DrewReese So what is the solution and the method? –  Jul 18 '21 at 07:05
  • I like the early return if state hasn't completed. Eg. `if (!quotes) return (
    Loading...
    ); return (.....`. You can also just return null if you don't require a loading indicator, so keeping initial state as null would be logical here.
    – Keith Jul 18 '21 at 07:30
  • I like the early return for a couple of reasons, 1. Using an empty array will still cause a pointless render, 2. It's nice and clean if you do want to implement a loading indicator. 3. using null your also getting more state jnfo for free, an empty default array here breaks that. – Keith Jul 18 '21 at 07:39
  • Does this answer your question? [Can't perform a React state update on an unmounted component](https://stackoverflow.com/questions/53949393/cant-perform-a-react-state-update-on-an-unmounted-component) – Martin Jul 18 '21 at 07:56

2 Answers2

1

Issue

The initial quotes state value is null, so it can't be mapped.

const [quotes, setQuotes] = useState(null);

Solution

Provide valid initial state, I suggest using an empty array ([]).

const [quotes, setQuotes] = useState([]);

Now you'll have valid quotes state that can be mapped on the initial render. Array.prototype.map can safely handle empty arrays.

{quotes.map(({id, quote}) => {
  return <p key={id}>{quote}</p>
})}
Drew Reese
  • 165,259
  • 14
  • 153
  • 181
0

As @DrewReese said you can either set the initial value of state as an empty array or choose to show quotes conditionally using:

    {quotes && quotes.map(({id, quote}) => {
        return <p key={id}>{quote}</p>
    })

What this code does is that it will check whether any value is there for quotes and if it's there only, it will call the quotes.map() function.

Update: We can also use optional chaining, which is more readable than conditional (short-circuiting as in above).

{quotes?.map(({id, quote}) => {
     return <p key={id}>{quote}</p>
 })

What the above code does is that it checks whether quotes are there and if it's there only, it will call the map function. If "quotes" is undefined, or null or any other falsy value, it will return the falsy value and stops execution.

The advantage of these solutions is that even if, in any case, quotes are not having an array it will not cause any issue, but won't execute that code. Thanks to @DrewReese for this solution.