Reputation: 71
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
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
Reputation: 144
Add inital user name as empty string
const [user, setUser] = useState<{ name: string }>({
name: ""
});
Upvotes: 0
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