Ilya Eru
Ilya Eru

Reputation: 71

React Typescript - Argument of type is not assignable to parameter of type 'SetStateAction<User | Record<string, never>>'

I have a simple React web app with data (List of object) of Users, Todos And Posts, where I show, update and delete the data.

I have a Component named UserView where I get the user data from the props, and then I save the data in the state with useEffect

interface UserViewProps {
  userData: User;
}

export default function UserView({
  userData,
...
}: UserViewProps) {
  const [user, setUser] = useState<User | Record<string, never>>({});
  useEffect(() => {
...
    setUser({ ...userData });
  }, [todosData]);

The problem is when I want to change the user state from input to pass to a callback function with destructuring:

<input
        onChange={(e) =>
          setUser({
            ...user,
            name: e.target.value,
          })
        }

I receive the following TypeScript error:

Argument of type '{ name: string; id: number; username: string; email: string; address: Address; phone: string; website: string; company: Company; } | { name: string; }' is not assignable to parameter of type 'SetStateAction<User | Record<string, never>>'.
Type '{ name: string; }' is not assignable to type 'SetStateAction<User | Record<string, never>>'.
Type '{ name: string; }' is not assignable to type 'Record<string, never>'.
Property 'name' is incompatible with index signature.
Type 'string' is not assignable to type 'never'.ts(2345)

When I change

 const [user, setUser] = useState<User | Record<string, never>>({});

to

  const [user, setUser] = useState<User | any>({});

Everything works How can I fix this TypeScript error?

Full code below:

import React, { useEffect, useState } from 'react';
import { Post, Todo, User } from '../../../helpers/utils';
import { StyledUserView } from './style';

interface UserViewProps {
  userData: User;
  todosData: Todo[];
  postsData: Post[];
  handleUserDelete: (userId: number) => void;
  handleUserUpdate: (updatedUser: User) => void;
}

export default function UserView({
  userData,
  todosData,
  postsData,
  handleUserDelete,
  handleUserUpdate,
}: UserViewProps) {
  const [isUncompletedTodo, setIsUncompletedTodo] = useState(false);
  const [isShowOtherData, setIsShowOtherData] = useState(false);
  const [user, setUser] = useState<User | any>({});
  useEffect(() => {
    if (todosData.find((todo) => todo.completed === false)) {
      setIsUncompletedTodo(true);
    } else {
      setIsUncompletedTodo(false);
    }
    setUser({ ...userData });
  }, [todosData]);
  return (
    <StyledUserView isUncompletedTodo={isUncompletedTodo}>
      ID: {user.id} <br />
      <label htmlFor={`${user.id}-name-input`}>Name:</label>{' '}
      <input
        onChange={(e) =>
          setUser({
            ...user,
            name: e.target.value,
          })
        }
        id={`${user.id}-name-input`}
        type="text"
        value={user.name}
      />{' '}
      <br />
      <label htmlFor={`${user.id}-email-input`}>Email:</label>{' '}
      <input
        onChange={(e) =>
          setUser({
            ...user,
            email: e.target.value,
          })
        }
        id={`${user.id}-email-input`}
        type="text"
        value={user.email}
      />{' '}
      <br />
      {isShowOtherData && (
        <section className="other-data">
          <label htmlFor={`${user.id}-street-input`}>Street:</label>{' '}
          <input
            onChange={(e) =>
              setUser({
                ...user,
                address: {
                  ...user.address,
                  street: e.target.value,
                },
              })
            }
            id={`${user.id}-street-input`}
            type="text"
            value={user.address.street}
          />{' '}
          <br />
          <label htmlFor={`${user.id}-city-input`}>City:</label>{' '}
          <input
            id={`${user.id}-city-input`}
            type="text"
            value={user.address.city}
            onChange={(e) =>
              setUser({
                ...user,
                address: {
                  ...user.address,
                  city: e.target.value,
                },
              })
            }
          />{' '}
          <br />
          <label htmlFor={`${user.id}-zip-code-input`}>Zip Code:</label>{' '}
          <input
            id={`${user.id}-zip-code-input`}
            type="text"
            value={user.address.zipcode}
            onChange={(e) =>
              setUser({
                ...user,
                address: {
                  ...user.address,
                  zipcode: e.target.value,
                },
              })
            }
          />{' '}
          <br />
        </section>
      )}
      <button
        onClick={() => {
          setIsShowOtherData(!isShowOtherData);
        }}
      >
        {isShowOtherData ? 'Hide Other Data' : 'Show Other Data'}
      </button>
      <button onClick={() => handleUserUpdate(user)}>Update</button>
      <button
        onClick={() => {
          handleUserDelete(user.id);
        }}
      >
        Delete
      </button>
    </StyledUserView>
  );
}

Upvotes: 2

Views: 7319

Answers (3)

Sam
Sam

Reputation: 1239

if the name is a variable then you need to do [name] else it is static then "name"

 <input
 onChange={(e) =>
  setUser(prv=>{
   ...prv,
    [name]: e.target.value,
   })
}

Upvotes: 0

Piotr
Piotr

Reputation: 144

Add inital user name as empty string

  const [user, setUser] = useState<{ name: string }>({
    name: ""
  });

code sanbox

Upvotes: 0

Kris1803
Kris1803

Reputation: 82

You can try to change

const [user, setUser] = useState<User | Record<string, never>>({});

// to 
const [user, setUser] = useState<User | Record<string, any>>({});

//or 
const [user, setUser] = useState<User | Record<string, string>>({});

Upvotes: 1

Related Questions