I have an nested object in React functional component state, but I only can access first level of object if I use any
type.
export default function Detail() {
const [user, setUser] = useState<any>({});
const { id } = useParams();
useEffect(() => {
fetch('http://localhost:4000/users/' id)
.then(response => {
return response.json();
})
.then(data => {
setUser(data);
})
}, []);
return (
<div>
<h1>Detail view</h1>
<p>ID: { user.id }</p>
<p>First name: { user.first_name }</p>
<p>Last name: { user.last_name }</p>
<p>Email: { user.email }</p>
<p>Gender: { user.gender }</p>
</div>
);
}
When trying to access user.company.name
it throws
Detail.tsx:40 Uncaught TypeError: Cannot read properties of undefined (reading 'name')
I made an interface for it, but it gives me an error if I'm trying to use it for state type.
interface UserInfo {
id: number;
first_name: string;
last_name: string;
email: string;
gender: string;
company: {
name: string;
department: string;
}
}
Argument of type '{}' is not assignable to parameter of type 'UserInfo | (() => UserInfo)'.
How can I use defined interface as state type?
CodePudding user response:
Don't use the any
type. TypeScript can't help you if you don't use actual types. Type the state narrowly.
Don't use an empty object as your default value. It isn't useful. Use a value that clearly indicates that the data isn't available yet.
type PossibleUserInfo = UserInfo | null;
const [user, setUser] = useState<PossibleUserInfo>(null);
Then, handle the case where you don't have the data yet explicitly.
It isn't useful to spit out a div with a bunch of paragraphs containing a label but not data.
if (user) {
return (
<div>
<h1>Detail view</h1>
<p>ID: { user.id }</p>
<p>First name: { user.first_name }</p>
<p>Last name: { user.last_name }</p>
<p>Email: { user.email }</p>
<p>Gender: { user.gender }</p>
</div>
);
}
return (
<div>
<h1>Default view</h1>
<Loading />
</div>
);
… where Loading
is a component that shows (for example) a loading spinner.