Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

this is the assignment of Michail Apostolou #6

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

cucaracha18421921
Copy link

No description provided.

@wiz-gwi
Copy link

wiz-gwi bot commented Sep 24, 2024

Wiz Scan Summary

Scan Module Critical High Medium Low Info Total
IaC Misconfigurations 0 0 0 0 0 0
Vulnerabilities 0 1 1 0 0 2
Sensitive Data 0 0 0 0 0 0
Secrets 0 0 0 0 0 0
Total 0 1 1 0 0 2

View scan details in Wiz

To detect these findings earlier in the dev lifecycle, try using Wiz Code VS Code Extension.

<>
<DescriptionStyled>{description}</DescriptionStyled>
{ id!== '' &&
<button onClick={(evt) => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you have to use event.stopPropagation? The CatDetailContainer has an action-less onClick event attached.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was interfering with the modal. CatDetail can be rendered either inside a modal or just as a single page. In the case of a modal the event propagating up to the modal level and closing it as a result

Copy link

@chbardamu chbardamu Sep 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm... Maybe that's because you have that empty event listener that is attached to the parent component. Otherwise, I can't see how the modal can be closed by clicking on part of its content.

fetchCatDescription();
}, [catId]);

const handleAddToFavorites = useCallback(async () => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How would you write that if you wanted to include the event as an argument?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would change line 110 to
const handleAddToFavorites = useCallback(async (evt: React.MouseEvent) => {

and line 161 to
handleAddToFavorites(evt);

const [showSucessMessage, setShowSucessMessage] = useState<boolean>(false);

useEffect(() => {
const fetchCatDescription = async () => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How come you chose to not use useCallback for this one, like you did with handleAddToFavorites?

Copy link
Author

@cucaracha18421921 cucaracha18421921 Sep 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to define this function inside useEffect, so I can use it when the component renders.

I can't put it in a useCallback since if you want your function to run from within a hook you have to define it inisde the hook. Inside the hook I can't use useCallback hook. I can't define the function outside as the only way to run an async function from the useEffect hook is to define within the hook.

As a result I can't memoize this function. If there is another way to achieve that I would be really happy to update it

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines 80 to 83
let {catId} = useParams();
if(catId === undefined && id !== '' ) {
catId = id;
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const catId = useParams().catId || props.catId;

...would be much clearer and indicate the origin of the catId value, which now is a bit obfuscated.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed it is much cleaner. This was missed. For debugging I had it expanded and didn't simplify it

margin-top: 10px;
`;

const CatDetailContainer = styled.div<{ $withModal?: boolean }>`
Copy link

@chbardamu chbardamu Sep 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Plain curiosity: Why using a '$' in naming? Angular reference? 😋

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reason for that is in order to know at first glance that this prop is used only for styling

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm. I don't get this. You use a styled component - what else could it be used for if not for styling?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When we render CatDetailContainer the variable withModal is only used for styling and doesn't play any part on the logic, that's why I named it starting with an $. But of course this depends on the code styling agreements

Comment on lines 28 to 29
<li data-testid="cat-image-container" key={catId} style={{ width: `${100 / imagesPerRow}%`, display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
<div

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No styled component?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was fixed on last commit

Comment on lines +8 to +15
const CatImageListContainer = styled.div`
position: relative;
`;

const CatImageContainer = styled.ul`
display: flex;
flex-wrap: wrap;
`;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed you do not separate styled components from regular ones, file-wise. Is there a reason for that?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As you can see before I had create a seperate component for styles of the index. I didn't do it in this case since my thoughts were that since these components are not re-used and they are not that many cluttering the file it would be easier to navigate on the project at its current size.

Of course for readability reasons and to make the testing coverage more precise, I would place them into {componentName}.styles.tsx files as the project was growing

Copy link

@chbardamu chbardamu Sep 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not just this case however, it's every other case. I understand reusability being a factor, but I'd still argue that the practice of keeping your styled components inside the same file is already causing a maintenance issue. Although, it is indeed sensible to make such changes as the app is growing, setting up a good foundation is essential, and I think this file separation is part of this foundation. Best practices are mostly for things that we may need to deal with later on, aren't they?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I totally agree with this comment and I will be happy to implement it. It's an approach I agree, but given the time this was a detail I left for later, but as you mentioned it is already reaching the point where readability is already hurt

Comment on lines 38 to 48
<RandomCatsTop
header="Random Cats"
imagesPerRow={imagesPerRow}
setImagesPerRow={setImagesPerRow}
getNewListOfCats={async ()=> {
localStorage.removeItem('cats');
const newCats = await getRandomCats();
setCats(newCats);
localStorage.setItem('cats', JSON.stringify(newCats));
}}
/>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you explain the logic of this component and why you choose to do it this way?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed this component does not follow common conventions, like the "getNewListOfCats", which appears to be an event ('onclick') action callback to a child component. It'd be expected to name it accordingly (eg. "onDisplayMoreCats").

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This component is the one that is the container of the screen that shows random Cats. The logic here is that the RandomCatsTop contains the ui that sets the number of images per row and gets a fresh list of cats.

This is necessary since I store in localStorage every request for 10 random cats. I store the requests in localStorage in order to make more seamless the experience of a user opening the CatDetail inside the App and not in a modal. In that case when the user was hitting back he/she would get a new set of 10 cats. For that reason I have implemented the button getNewListOfCats in order to clean your local storage

I am totally in agreement that it should be named with a name starting with 'on' to indicate the action

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. In that case, you think using localStorage is better than sessionStorage?

How would you clear the history of loaded cat images for the user to start all anew when they reopen the app? Assuming this was an actual spec/requirement.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you are right. SessionStorage should be better as long there's no strict requirement needing to persist data even after the tab is closed.

I could use localStorage.removeItem(key); This looks implicit and it only removes the specific item.

Comment on lines +113 to +117
setShowSucessMessage(true);
setShowErrorMessage(false);
}else{
setShowErrorMessage(true);
setShowSucessMessage(false);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't it a bit redundant to have to vars on the component's state for something that can easily be done using just one? Let alone the unnecessary re-renderings this causes.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would change lines 164, 165 to
{ message !== null && message == 'failed' && <InformationText>Could not save to favorites</InformationText>} {message !== null && message == 'success' && <InformationText>Saved to favorites</InformationText>}

I would remove lines 89, 90 in favor of const [message,setMessage] = useState<string>('');

and lines 112-118 will be replaced by

setMessage((successFullySaved?'success':'failed'));

Naming is a bit generic, but for the shake of passing the idea let's call the variable message

}
};

const getDataForBreed = async ( breedId?: string ):Promise<BreedDetails | undefined> => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this function ever return undefined?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes if it throws an exception

Comment on lines +18 to +19
headers: {
'x-api-key': 'live_Ka5tWZF7c0Gm8UhINCls6XSG8wmnHHVyxF8gFnQcnXXl4zPTA8RZcAWSsnpCphEm'
Copy link

@chbardamu chbardamu Sep 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you choose to use this api key this way? And by that, I mean directly inside each of the functions that require it to perform the corresponding requests.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. It was not in the requirement any detail on the authentication of that or how to retrieve the key. If we had a single key that was represented by our service I would put it as an env variable that I could set on my deployment pipeline. This variable can also be a secret ( like it is possible with GithubActions )

Comment on lines +48 to +49
alt = {`Breed ${breed.name} ${index + 1}`} style={{ maxWidth: '100%', height: 'auto' }}
onClick = {() => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You fallback to inline styling instead of using styled components in several cases. Why is that?

Comment on lines +63 to +66
const newCats = await getRandomCats();
const allCats = [...cats, ...newCats];
setCats(allCats);
localStorage.setItem('cats', JSON.stringify(allCats));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bit dirty. You could use a separate callback.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed I should have extracted into a function that is wrapped with useCallback. This was missed as well

@chbardamu
Copy link

I see you haven't used a code formatter. Is there a reason for that?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants