Skip to content Skip to sidebar Skip to footer

Chai Cannot Read Property 'then' of Undefined

React - Cannot read property 'map' of undefined

March 12, 2020 - 5 min read

If you lot are a react programmer, there is a good risk that you faced this error couple of times:

TypeError: Cannot read belongings 'map' of undefined

TL;DR - If yous are non in the mode for reading or y'all just want the bottom line, and so hither information technology is

The problem

In guild to understand what are the possible solutions, lets beginning sympathize what is the exact issue here.

Consider this code cake:

                          // But a data fetching office              const              fetchURL              =              "https://jsonplaceholder.typicode.com/todos/"              ;              const              getItems              =              (              )              =>              fetch              (fetchURL)              .              then              (              res              =>              res.              json              (              )              )              ;              part              App              (              )              {              const              [items,              setItems]              =              useState              (              )              ;              useEffect              (              (              )              =>              {              getItems              (              )              .              then              (              data              =>              setItems              (data)              )              ;              }              ,              [              ]              )              ;              render              (                                                <div                >                                                        {items.              map              (              item              =>              (                                                <div                key                                  =                  {item.id}                                >                            {item.title}                                                </div                >                            )              )              }                                                                            </div                >                            )              ;              }                      

Nosotros take a component that manage a state of items, information technology too have an issue which inside information technology we run an asynchronous operation - getItems, which will return us the data nosotros demand from the server, then nosotros phone call setItems with the received data every bit items. This component also renders the items - it iterate over it with .map and returning a react element for each detail.

But we wont run into anything on the screen, well except the error:

TypeError: Cannot read belongings 'map' of undefined

What's going on here?

We do have an items variable:

                          const              [items,              setItems]              =              useState              (              )              ;                      

And we did populate it with our data returned from the server:

                          useEffect              (              (              )              =>              {                              getItems                (                )                .                then                (                information                =>                setItems                (data)                )                ;                            }              ,              [              ]              )              ;                      

Well lets examine how the react flow looks similar in our example:

  1. React renders (invoking) our component.
  2. React "see" the useState call and return u.s.a. [undefined, fn].
  3. React evaluate our return statement, when it hits the items.map(...) line its really running undefined.map(...) which is obviously an fault in JavaScript.

What about our useEffect call though?

React will run all effects after the render is committed to the screen, which means we can't avoid a first render without our data.

Possible solutions

#1 Initial value

I possible solution is to requite your variable a default initial value, with useState it would look similar that:

                          const              [items,              setItems]              =              useState              (              [              ]              )              ;                      

This means that when react runs our useState([]) call, it will return the states with

Which ways that in the start render of our component, react will "run into" our items as an empty assortment, and so instead of running undefined.map(...) like earlier, it will run [].map(...).

#2 Conditional rendering

Another possible solution is to conditionally render the items, meaning if nosotros take the items then return them, else don't render (or render something else).

When working with JSX nosotros tin can't simply throw some if else statements within our tree:

                          // ⚠️ wont piece of work!!              export              default              role              App              (              )              {              // ....              return              (                                                <div                >                                                                      {                              if                (items)                {                                            items.                map                (                item                =>                (                                                                                  <div                  fundamental                                      =                    {item.id}                                    >                                {detail.title}                                                      </div                  >                                                            )                )                                            }                            }                                                                                          </div                >                            )              ;              }                      

But instead we can create a variable outside our tree and populate information technology conditionally:

Notation that we removed the initial array for items.

                          function              App              (              )              {              const              [items,              setItems]              =              useState              (              )              ;              useEffect              (              (              )              =>              {              getItems              (              )              .              then              (              information              =>              setItems              (information)              )              ;              }              ,              [              ]              )              ;                              let                itemsToRender;                                            if                (items)                {                                            itemsToRender                =                items.                map                (                item                =>                {                                            render                                                      <div                  fundamental                                      =                    {particular.id}                                    >                                {detail.title}                                                      </div                  >                                ;                                            }                )                ;                                            }                                            render                                                      <div                  >                                {itemsToRender}                                                      </div                  >                                ;                            }                      

The undefined or aught values are ignored inside the context of JSX and so its safe to pass it on for the outset render.

We could also employ an else argument if we desire to render something else similar a spinner or some text:

                          function              App              (              )              {              const              [items,              setItems]              =              useState              (              )              ;              useEffect              (              (              )              =>              {              getItems              (              )              .              and then              (              data              =>              setItems              (data)              )              ;              }              ,              [              ]              )              ;              allow              itemsToRender;              if              (items)              {              itemsToRender              =              items.              map              (              item              =>              {              return                                                <div                key                                  =                  {item.id}                                >                            {item.championship}                                                </div                >                            ;              }              )              ;                              }                else                {                                            itemsToRender                =                "Loading..."                ;                                            }                            return                                                <div                >                            {itemsToRender}                                                </div                >                            ;              }                      

#2.5 Inline provisional rendering

Some other option to conditionally render something in react, is to use the && logical operator:

                          function              App              (              )              {              const              [items,              setItems]              =              useState              (              )              ;              useEffect              (              (              )              =>              {              getItems              (              )              .              so              (              data              =>              setItems              (data)              )              ;              }              ,              [              ]              )              ;              render              (                                                <div                >                                                                                          {items                &&                items.                map                (                particular                =>                {                                            render                                                      <div                  key                                      =                    {detail.id}                                    >                                {item.title}                                                      </div                  >                                ;                                            }                )                }                                                                                                          </div                >                            )              ;              }                      

Why it works? The react docs explains it well:

It works because in JavaScript, true && expression ever evaluates to expression, and false && expression e'er evaluates to faux. Therefore, if the status is truthful, the element right afterwards && volition announced in the output. If it is false, React will ignore and skip it.

We can also utilize the provisional operator condition ? true : false if we want to render the Loading... text:

                          function              App              (              )              {              const              [items,              setItems]              =              useState              (              )              ;              useEffect              (              (              )              =>              {              getItems              (              )              .              so              (              information              =>              setItems              (data)              )              ;              }              ,              [              ]              )              ;              return              (                                                <div                >                                                                                          {items                              ?                items.                map                (                particular                =>                {                                            return                                                      <div                  primal                                      =                    {item.id}                                    >                                {particular.title}                                                      </div                  >                                ;                                            }                )                                            :                "Loading..."                }                                                                                                          </div                >                            )              ;              }                      

Nosotros can also mix both solutions, i.due east: initial value with provisional rendering:

                          function              App              (              )              {                              const                [items,                setItems]                =                useState                (                [                ]                )                ;                            useEffect              (              (              )              =>              {              getItems              (              )              .              and so              (              data              =>              setItems              (information)              )              ;              }              ,              [              ]              )              ;              render              (                                                <div                >                                                                                          {items                &&                items.length                >                0                                            ?                items.                map                (                item                =>                {                                            render                                                      <div                  key                                      =                    {item.id}                                    >                                {item.championship}                                                      </div                  >                                ;                                            }                )                                            :                "Loading..."                }                                                                                                          </div                >                            )              ;              }                      

Though keep in heed, whenever conditions become too complex, it might be a signal for u.s.a. to extract that logic to a component:

                                          function                List                (                                  {                  items,                  fallback                  }                                )                {                                            if                (                !items                ||                items.length                ===                0                )                {                                            return                fallback;                                            }                else                {                                            return                items.                map                (                detail                =>                {                                            return                                                      <div                  key                                      =                    {item.id}                                    >                                {item.title}                                                      </div                  >                                ;                                            }                )                ;                                            }                                            }                            function              App              (              )              {              const              [items,              setItems]              =              useState              (              [              ]              )              ;              useEffect              (              (              )              =>              {              getItems              (              )              .              then              (              data              =>              setItems              (data)              )              ;              }              ,              [              ]              )              ;              return              (                                                <div                >                                                                                                                                <                    Listing                                    items                                      =                    {items}                                    fallback                                      =                    {                    "Loading..."                    }                                    />                                                                                                                          </div                >                            )              ;              }                      

Wrapping up

When we get such an error, we are probably getting the value in an asynchronous way. Nosotros should provide an initial value for our variable or conditionally render information technology or both. If our condition become too circuitous, it might be a adept fourth dimension to excerpt the logic to a component.

Promise you found this article helpful, if you take a different approach or any suggestions i would love to hear about them, yous can tweet or DM me @sag1v. 🤓

taoworkly.blogspot.com

Source: https://www.debuggr.io/react-map-of-undefined/

Post a Comment for "Chai Cannot Read Property 'then' of Undefined"