{isLoading
@@ -97,12 +103,116 @@ export const UserBookmarks: FunctionComponent
= ({ user }) => {
}
```
-It's a reeeeeeally long component that does many things, (FYI it is a simplified version of a component in one of my project). Most of the time, the tech lead freezes seeing how many things we need to talk to. Let's break it down piece by piece.
+It's a reeeeeeally long component that does many things. Most of the time, the tech lead freezes seeing how many things we need to talk to. Let's break it down piece by piece.
+
+## The name
+
+The easiest part: it has a name, maybe it's not perfect but `UserBookmarks` make sense for me. Let's create its CRC card knowing that the only code calling it is the main page.
+
+
+
+That's a start!
## Inputs and output
-The `UserBookmarks` component takes a `user` prop and return a list of styled bookmarks with the possibility to add bookmark via a modal.
+The `UserBookmarks` component takes a `user` prop and return a list of styled bookmarks with the possibility to add bookmark via a modal. I'll say something positive about this component, it's that it is pretty convenient for the parent calling it, just pass it the user, it'll give you all the user bookmarks and more. Maybe too much.
-Cool! That's the first thing we can note on our CRC card.
+But hey! We can already note the first responsability on our CRC card.
-
+
+
+As we are talking about inputs, I want to know how the props are used. Are they necessary? Are they sufficient? Here we can see that `user` is only used for fetching data:
+
+```ts
+...
+const userBookmark = await fetch(`/users/${user.id}/bookmarks`, {
+...
+```
+
+Only the `user id` is necessary, why not just give the userId instead of the whole object?
+
+> Here is a classic responsibility leak I frequentely see. Components don't care that user have a `user.address.line1` property when they only want their `id`.
+
+That will be our first simplification.
+
+```tsx
+interface Props {
+ userId: number
+}
+
+export const UserBookmarks: FunctionComponent = ({ userID }) => {
+ const [bookmarks, setBookmarks] = useState([])
+ const [showAddBookmarkModal, setShowAddBookmarkModal] = useState(false)
+ const [isLoading, setIsLoading] = useState(false)
+
+ useEffect(() => {
+ setIsLoading(true)
+
+ try {
+ const userBookmark = await fetch(`/users/${userId}/bookmarks`, {
+ method: 'GET'
+ })
+ setBookmarks(userBookmarks)
+ } catch (error) {
+ setBookmarks([])
+ } finally {
+ setIsLoading(false)
+ }
+ }, [])
+
+ const addBookmarkToUser = async ({ bookmark }) => {
+ setIsLoading(true)
+ try {
+ const newBookmark = await fetch(`/users/${userId}/bookmarks`, {
+ method: 'POST',
+ body: JSON.stringify({ bookmark })
+ })
+ setBookmarks([...bookmarks, newBookmark])
+ } catch (error) {
+ console.warn(error);
+ } finally {
+ setIsLoading(false)
+ }
+ }
+
+ const tilesAnimation = gsap.to({
+ duration: 0.8,
+ opacity: 0.35,
+ yoyo: true,
+ repeat: -1,
+ stagger: 0.025,
+ })
+
+ return
+
+ {isLoading
+ ?
+ :
}
+
+
setShowAddBookmarkModal(true))}
+ alt={'strings['frontoffice.bookmark.add_bookmarks']'}
+ image={}
+ />
+
+ setShowAddBookmarkModal(false)}
+ bookmarks={bookmarks}
+ onBookmarkAdd={addBookmarkToUser}
+ />
+
+}
+```
+
+## The secret sauce
+
+Now comes where we'll definetely challenge how the component does its magic.