-2

I want to map an array from react's useState hook, but I get the error:

TypeError: documents.map is not a function

This is my code:

const [docs, setDocs] = useState(documents);

const rows = documents.map((doc) => (
    <tr key={doc.id}>
      <td>
        <Group spacing="sm">
...

where "documents" comes from props.

I console-logged docs and it prints out an array. What am I missing here? Is it, because "docs" is a state value?

To further clarify: I fetch the documents from supabase and want to integrate a realtime subscription.

Whenever I get a change in the db, the useEffect function triggers the setState function. Then the error appears. So I am not sure how to handle this with default values.

  • You said you `console.log(docs)`, but did you `console.log(documents)`? Is it _ever_ `undefined` maybe in an in flux state? – zero298 Sep 14 '22 at 15:00
  • It should work if documents is an array. Maybe it is undefined at first, then gets its value. – Meerkat Sep 14 '22 at 15:01
  • I am fetching the data from supabase - so async. Is there a way to "await" it? – Florian Engl Sep 14 '22 at 15:02
  • I believe you have to use traditional looping methods with asynchronous functionality. – mykaf Sep 14 '22 at 15:06
  • Consider giving your state a default value like `const Foo = ({documents=[]}) => {...}` that way it can't be undefined. – zero298 Sep 14 '22 at 15:18

4 Answers4

1

If you want to map documents from useState hook, then you shouldn't map documents (since this is an initializer, not the state value). Try this:

const [docs, setDocs] = useState(documents || []);

const rows = docs.map((doc) => <tr key={doc.id}>...
dr_leevsey
  • 365
  • 4
  • 15
  • Thanks for the reply. I changed my code to your suggestion, but on the realtime change I still get the same error. – Florian Engl Sep 14 '22 at 16:01
  • then you have to give more detailed example, can you make a complete test example e.g. in codesandbox or similar service and provide a link? StackOverflow also has code editor so you can embed working code into your question – dr_leevsey Sep 14 '22 at 16:08
  • Here is a link to the 2 relevant files: index.js and DocumentTable.js https://codesandbox.io/s/trusting-glitter-5e5yw2?file=/components/DocumentsTable.js – Florian Engl Sep 14 '22 at 16:22
  • what I meant to was working minimal example, not broken piece of real code... from what I see there this part confusing me the most: `setDocs(...documents, payload.new);` if you're trying to append `payload.new` to existing array you should do `setDocs([...documents, payload.new]);` – dr_leevsey Sep 14 '22 at 16:48
0

I tend to agree with @zero298. It's likely at some point documents still isn't fully defined. Try using a boolean check:

const rows = !!documents && documents.map((doc) => {});

!!documents will return truthy or falsy. If falsy, rows will just equal false, otherwise your .map() will proceed.

wrxsti
  • 3,434
  • 1
  • 18
  • 30
0

Since documents come from props, you need to apply a condition on the render of this component in its parent, like so:

  {documents && documents.length > 0 && <Child documents={documents} />}

Or you could give the component a defaultProps for documents like this:

Component.defaultProps {
  documents:[]
}

Where Component is the name of your component.

Zakaria Benali
  • 298
  • 2
  • 5
0

If the documents property is from props you have to check it either it is undefined or not. It can be done if you use it inside useEffect hook. pass documents as dependency injection and add if condition when document is not undefined then you can call a function which will loop through documents using map

useEffect(() => {
if(props.documents !== undefined && props.documents.length > 0)
   {  /** Add logic here **/}
}, [props.documents])
Marium
  • 11
  • 2