useState: simple state values
when initial value is only know at a future point in time
this is because use can be null, so only if user exists, access the name property
always check before accessing the property
use type assertion to let ts know that user is always of type User and won't be null
this can allow us to access name without a check
useReducer: for complex state logic, for example next state depends on previous state
strict type with string literals
use context: to consume the context value
1. context already knew
2. context is a future value
provide the user and the function to set user when they log in or log out
useRef senarios:
1. as a read-only ref for dom element
use type assertion ! if you know it is not null when accessing it to avoid optional chaining ?
2. as a mutable value which can behave like an instance variable