- Config route list to specify which menu requires login
- Render route list based on login status
- Integrate with login and test
- Target to have type suggestion when using profile from useAuth()
- TIP: Organize Imports (Option + Shift + O)
Current flow:
- On init: useAuth with default data =
undefined
- Call API profile and update data = logged in user
Updated flow:
- Save user's info to local storage if login successfully
- On init: useAuth with default data =
user info from local storage
- Call API profile and update data = logged in user
- Remove local storage if logout
key: user_info
Unexpected cases handling:
- What if fetch profile failed?
Process:
- Server side generate HTML (A) and send to client
- Client get HTML (A) to display on UI and download JS in the background
- Once JS downloaded, it will be executed. Hydration process take place and generate a new DOM (B). Then it try to match B and A and attach event listener to it.
If A = B --> OK If A <> B --> Show error text content did not match
Solutions:
- Make sure the first render on client side should be the same with server side.
- Use client side rendering via dynamic feature of NextJS (not SEO friendly)
Refs:
- https://nextjs.org/docs/messages/react-hydration-error
- https://www.joshwcomeau.com/react/the-perils-of-rehydration/
- https://blog.saeloun.com/2021/12/16/hydration
- https://thanhle.blog/blog/server-side-rendering-voi-hydration-lang-phi-tai-nguyen-nhu-the-nao
- https://nextjs.org/docs/advanced-features/dynamic-import
- Can't change tsconfig from
jsx: preserve
tojsx: react
- Can safely remove React import due to this post
- Can't use named export with ssr: false --> change to use default import instead
Refs:
- Mock response to return Error instead of success
- How to extract error body from API response
- Throw error response in axios interceptor
- Retrieve error message in catch statement
- Do whatever you want with the message (show toast, log, report error, ...)
- add react-toastify package
- toast error message
Ref: https://kentcdodds.com/blog/get-a-catch-block-error-message-with-typescript
- Works API
- OpenAPI (Swagger) Editor
- Where to get API Schema file? --> from my github repo
- Demo how to use Swagger directly in VSCode
- add work-api.ts
- add type definition for work api: ListResponse, ListParams
- call API and log data on component
export interface Pagination {
_page: number
_limit: number
_totalRows: number
}
- update Pagination type:
_total
to_totalRows
- update
swr
lib to latest v2.1.2 - implement useWorkList() hook using useSWR()
- try to use the hook in component
- Show Work List UI
- demo decouple interval
- List State
- Loading
- No data
- Has data
- Component:
<Skeleton />
docs - Update
<WorkList />
to show loading status
- Using Component
<Pagination />
docs - Integrate our API response with Pagination component
- Disable Prev / Next when page is at min / max.
export default function PaginationControlled() {
const [page, setPage] = React.useState(1);
const handleChange = (event: React.ChangeEvent<unknown>, value: number) => {
setPage(value);
};
return (
<Stack spacing={2}>
<Typography>Page: {page}</Typography>
<Pagination count={10} page={page} onChange={handleChange} />
</Stack>
);
}
- Add
<WorkFilters />
component (clone from<LoginForm />
)- take care of all filters: search, categories select, ...
- take initial value to set default value for each filter
- Support
externalOnChange
for<InputField />
- Add debounce on search change
- Log form submission value
- Log data received at page-level component
PageA: control form submit logic |__ WorkFilters: manage filters form and notify parent via callback if any changes | |__ InputField: search | |__ SelectField: category select
PageB: control form submit logic |__ WorkFilters: manage filters form and notify parent via callback if any changes | |__ InputField: search | |__ SelectField: category select
Current flow: refetch work list whenever filters state
changes
Current flow: when filters change --> update state
(setFilters)
New flow: refetch work list whenever router query
changes
New flow: when filters change --> update router query
- remove filters state
- using Shallow Routing
- how to set default values for
WorkFilters
- how to ignore the first render with empty query?
- fixed: API resolved without sending a response for /api/works?_page=1&_limit=3, this may result in stalled requests.
- Goal: will
show page loading
by default (both on server and client) - Using tool: Performance Insights
Process:
- Server return a HTML
A
- Client render first time
B
- router.isReady = false
, router.query = undefined - Client render second time - router.
isReady = true
, router.query = data
<Skeleton
variant="rectangular"
height={40}
sx={{
display: 'inline-block',
width: '100%',
mt: 2,
mb: 1,
verticalAlign: 'middle', // fix layout shift
}}
/>
A Form Control includes 2 main parts:
- UI control
- Bind form state to UI control (react-hook-form) --> integrate with form logic
- Add Autocomplete UI control
- Show it on UI to see how it looks like
- Add new component:
AutocompleteField
(cloned fromInputField
)
- Add type definition for AutocompleteField
- Add new key to WorkFiltersPayload: tagList_like
- Add generic type for InputField
- Add generic type for AutocompleteField
- Integrate with react hook form control
- Binding form state: onChange, onBlur, ref, value, error
-
add new api file: api-client/tag-api.ts GET: /api/tags?_page=1&_limit=30
-
new hook file: hooks/use-tag-list.ts
-
Populate data to AutocompleteField
- filter works by tags (either tag1 or tag2 or tag3):
GET /api/works?_page=1&_limit=10&
tagList_like=tag1|tag2|tag3
- transform form data into api payload
const formData = { search: '', selectedTagList: ['Design', 'Dashboard'] }
// 1. turn selectedTagList into tagList_like (array to string using join())
// 2. remove unused attr selectedTagList
const apiPayload = { search: '', tagList_like: 'Design|Dashboard' }
- set initial value for auto complete field