1

Im new to graphql so I decided to follow this guide.

However I got an error when querying (step 7) :

Argument of type '({ __typename?: "User"; } & Pick<User, "email" | "id" | "name" | "createdAt">)[]' is not assignable to parameter of type '(prevState: undefined) => undefined'.
  Type '({ __typename?: "User"; } & Pick<User, "email" | "id" | "name" | "createdAt">)[]' provides no match for the signature '(prevState: undefined): undefined'.  TS2345

    37 |       if (data && data.allUsers) {
    38 |         if (data && data.allUsers && data.allUsers.nodes) {
  > 39 |           setUsers(data.allUsers.nodes);
       |                    ^
    40 |         }
    41 |       }
    42 |     }

Here is the component:

import React, { useState, useEffect } from 'react'
import { useGetUsers } from '../utils/services'
import { User } from '../generated/graphql'

const Users: React.FC = () => {
    const [users, setUsers] = useState()
    const { data, error, loading } = useGetUsers()
    
    useEffect(() => {
        if (data) {
            if (data && data.allUsers && data.allUsers.nodes) {
                setUsers(data.allUsers.nodes)
            }
        }
        if (error) {
            console.log(error)
        }
        if (loading) {
            console.log(loading)
        }
    }, [data, error, loading])

    return (
        <>
            <h2>Hello users,</h2>
            {users &&
                users.length > 0 &&
                users.map((user: User, index: number) => (
                    <p key={`user_${index}`}>
                        {user.name}{' '}
                    </p>
                ))}
        </>
    )
}

export default Users

However it works when I use

data.allUsers.nodes

Like this :

{data && data.allUsers ? (
        data.allUsers.nodes &&
        data.allUsers.nodes.length > 0 &&
        data.allUsers.nodes.map((user: User, index: number) => (
          <p key={`user_${index}`}>{user.name}</p>
        ))
      ) : (
        <React.Fragment />
      )}

And of course I've removed setUsers in the useEffect() hooks.

I notice that I had to set strict to false in my .tsconfig.json file.

I would like to know why I m not able to use the setUsers hooks? Why I got that error and how to solve it?

Thank you!

user-id-14900042
  • 686
  • 4
  • 17
bdeweer
  • 135
  • 1
  • 14
  • no need to duplicate data in local state ... just use `const users = loading ? [] : data.allUsers.nodes;` or `const users = data ? [] : data.allUsers.nodes;`before return – xadm Nov 01 '20 at 20:52

2 Answers2

2

Possibly:

const [users, setUsers] = useState()

should be:

const [users, setUsers] = useState([])
Ben Stephens
  • 3,303
  • 1
  • 4
  • 8
2

When you call useState without setting an initial value, typescript doesn't know what value that state is expected to be. You need to declare it explicitly by setting the generic.

const [users, setUsers] = useState<User[]>();

Now typescript knows that our state can only be an array of User objects or undefined.

If we want to exclude the possibility of undefined, we can initialize it with an empty array []. We still need to provide the generic to specify what type of elements are in the array. But now users is always an array of zero or more User objects.

const [users, setUsers] = useState<User[]>([]);

So of your runtime checks become unnecessary when we have proper typings. With the useState line above, your return becomes:

return (
    <>
        <h2>Hello users,</h2>
        {users.map((user, index) => (
            <p key={`user_${index}`}>
                {user.name}{' '}
            </p>
        ))}
    </>
)

It's ok to map on empty array, so we don't need to check anything. We also don't need to declare the types for user and index because they are already known based on the type of the users array.

Linda Paiste
  • 38,446
  • 6
  • 64
  • 102