Let's suppose I have the following LoadingIndicator
component:
export default function LoadingIndicator({ isLoading, children }) {
return isLoading ? (
<div>
<LoadingContainer>
<Spinner as="span" animation="border" role="status" />
<br />
<span>Carregando...</span>
</LoadingContainer>
</div>
) : (
{ children }
);
}
Now I have the following example of a page that uses this component:
export default function DocumentsPage() {
const dispatch = useDispatch();
const currentUser = useSelector((state) => state.users.current);
React.useEffect(() => {
if (!currentUser) dispatch(UsersAction.requestCurrentUser());
}, [dispatch, currentUser]);
return (
<LoadingIndicator isLoading>
<MyAccountBaseLayout>
<Wrapper>
{currentUser.documentsStatus.map((document) => (
<DocumentCard
key={document.label}
label={document.label}
status={document.status}
/>
))}
</Wrapper>
</MyAccountBaseLayout>
</LoadingIndicator>
);
}
The idea is simple: render the loading spinner stuff when isLoading, otherwise render what we received. However, currentUser
will only be present after the requestCurrentUser
call, which happens after the first render. Because I iterate over the currentUser.documentsStatus
, I'm receiving the error TypeError: Cannot read property 'documentsStatus' of null
.
What I imagined that should happen is that the piece of code that uses the currentUser
variable should only be rendered when the prop isLoading
is false, but that's wrong. My question is: with the internal logic of the LoadingIndicator
, why the error still happen? What is the best approach here?
In your case, it will always fall into this error since there isn't any initial value for currentUser.documentsStatus
in your state (probably). You can set an initial state but I guess you don't want to do this. In this case instead of doing the map
directly there, maybe you can pass this data to another component and do it there.
return (
<LoadingIndicator isLoading={isLoading}>
<MyAccountBaseLayout>
<Wrapper>
<DocumentCards currentUser={currentUser} />
</Wrapper>
</MyAccountBaseLayout>
</LoadingIndicator>
);
function DocumentCards({ currentUser }) {
return currentUser.documentsStatus.map((document) => (
<DocumentCard
key={document.label}
label={document.label}
status={document.status}
/>
));
}
I don't know if this is the strategy you want but without setting an initial state or checking the data before map
(you don't want to do this probably and use the loading part I guess) you don't have so many options.
By the way, I don't know where do you get isLoading
but <LoadingIndicator isLoading>
means isLoading
will always be true in this component. This is why I use it like <LoadingIndicator isLoading={isLoading}>
in my example, assuming there is an isLoading
defined somewhere.
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments