It was easy, but a good challenge. It's the first time I've combined React with Tailwind and I'm happy with the result. I'm still not entirely sure how data fetch works, but I managed to get it done.
Fazza Razaq Amiarso
@fazzaamiarsoAll comments
- @SoulRvr29Submitted over 1 year ago@fazzaamiarsoPosted over 1 year ago
Hi @SoulRvr29! Great work on your project, the README is fantastic!
You can use 1 state for this project. Example
// `advice` become an object const [advice, setAdvice] = useState({}); // then you can get the advice data const adviceContent = advice.advice; const adviceId = advice.id useEffect(() => { clickHandler(); }, []); const clickHandler = () => { fetch("https://api.adviceslip.com/advice") .then((response) => { if (response.ok) { return response.json(); } else { console.log("no data"); } }) .then((data) => { setAdvice(data.slip) }) .catch((err) => { console.log("Something went wrong!", err); }); };
I hope it helps! Cheers!
Marked as helpful1 - @JustANippleSubmitted over 1 year ago
Hi everyone! i made it through this challenge!
The hardest part was to manage states for every component and passing everything to the "add to cart" button to fill the cart list
Hope you will like it!
I would like to get any tip on how to improve my solution!
@fazzaamiarsoPosted over 1 year agoHi Sam! Great job on completing the project!
I have a quick tip for you.
The vote state is excessive because you can derive the
Vote
value from the cart's quantity. Here's how// `cart` is cartItems and `setCart` is setCartItems const Vote = ({ cart, setCart }) => { function handlePlusClick() { // increment the quantity directly setCart((prevCart) => ({ ...prevCart, quantity: prevCart.quantity + 1 })); } function handleMinusClick() { if (cart.quantity <= 0) return; // decrement the quantity directly setCart((prevCart) => ({ ...prevCart, quantity: prevCart.quantity - 1 })); } return ( <div className={styles.container_vote}> <button className={styles.vote_plus} onClick={handleMinusClick}> <img src="icon-minus.svg" alt="downvote" /> </button> <p className={styles.vote_score}>{cart.quantity}</p> <button className={styles.vote_minus} onClick={handlePlusClick}> <img src="icon-plus.svg" alt="vote" /> </button> </div> ); };
I hope it helps you! Cheers!
Marked as helpful0 - @Zy8712Submitted over 1 year ago
Very fun project. Hoping to get some advice with localstorage and how I can use things such as .env, backend api's, etc in order to protect API Key's and other various things. Thanks.
@fazzaamiarsoPosted over 1 year agoHi @Zy8712! Nice solution!
Since you use Vercel to deploy your project, you can take a look at Vercel Serverless function to create a simple API endpoint which have access to Environment Variable. Documentation Reference.
I also highly recommend you to use a bundler like Vite for your projects.
I hope it helps! Cheers!
0 - @outerpreneurSubmitted over 1 year ago
My second project with tailwindss how do you do grid template areas with Tailwind CSS? This is the only way I have found out. The slider has a working solution at the moment but is not yet perfect
@fazzaamiarsoPosted over 1 year agoHi @outerpreneur!
I think Tailwind doesn't have support for grid template area. If you really want to do it with template area, then you may have to resort to write custom CSS. Docs reference.
Cheers!
Marked as helpful0 - @MrDannieSubmitted over 1 year ago
Hi guys!👨💻 I'm super exited completing this challenge! I've a learnt a whole lot as this is first largest app I have built using React and Tailwind (I have always been an Angular developer). I encountered a lot of challenges, bugs and errors in the process of developing this application, which I learnt a lot from. And also thanks to Google, youtube & stack overflow, I would probably had been stuck on some errors forever 😂 :)
Some of major areas I encountered challenges while developing this application were State Management & Passing data btw components. However I believe was able to do a good Job at this as it pushed me to learn how to effectively utilize tools like Context Api, useRef, useEffect, props and callback.
I will love to receive feedbacks from the community :
- How long did it take you to complete this project (it took me 1 and half month)?
- What was the biggest thing you learnt building this project?
- What were your most difficult areas?
Pls kindly also rate my work on a scale of 1-10, I would be also pleased to know what areas I should in improve on?
Thanks! 😊
@fazzaamiarsoPosted over 1 year agoHi @MrDannie! It's awesome that you complete this complex project while learning a new framework!
I just have a couple suggestions for you:
- I noticed that you use
react-hooks-global-state
for Global State. I suggest to take a look atJotai
which do the same thing and same maintainer, but still active and maintained. - For some side-effects like inserting item into LocalStorage, it's better to first do it inside Event Handler, rather than
useEffect
that can be tricky to track. You can refer to this docs. For example inApp.jsx
// instead of this useEffect(() => { localStorage.setItem("BoardData", JSON.stringify(boardData)); }, [boardData]); // do this const updateAppData = (data) => { setAppBoardData(data); // here, what trigger the side effect is immediately obvious localStorage.setItem("BoardData", JSON.stringify(data)); };
I hope it helps! Cheers!
Marked as helpful0 - @reymartvigoSubmitted over 1 year ago
Hi this is my solution to Job Listing w/ Filtering.
Feel free to give feedbacks and suggestions on how I can improve are very welcome!
@fazzaamiarsoPosted over 1 year agoHi ! Nice Solution!
I have some quick tips for you
- For deleting item, you can use
.filter
to avoid array mutation.
const handleDeleteItem = (itemToDelete) => { const updatedItems = selectedItems.filter(item => item !== itemToDelete) setSelectedItems(updatedItems); setFilterVisible(updatedItems.length !== 0) }
- You can utilize spread operator to pass props to a component if the props is the same as the object. Example
// instead of this <Jobs key={job.id} id={job.id} company={job.company} logo={job.logo} new={job.new} featured={job.featured} position={job.position} role={job.role} level={job.level} postedAt={job.postedAt} contract={job.contract} location={job.location} languages={job.languages} tools={job.tools} openFiltered={handleOpenFilter} // more concise, with the tradeoff of must lookup `job` to know what you passing <Jobs key={job.id} {...job} openFiltered={handleOpenFilter} />
I hope it helps! Cheers!
Marked as helpful0 - For deleting item, you can use
- @Leone-RicardoSubmitted over 1 year ago
In this challenge, I changed some parts to make it as professional as possible, and one of the features I added was the success message that appears after completing the registration, it was not in the original documentation, but I thought it would look more stylish this way haha. Better solutions and tips are always welcome!
@fazzaamiarsoPosted over 1 year agoHi @Leone-Ricardo! Nice solution and great addition!
I have some suggestion for you
- For Submit Button, it's better to use a
<button>
as it can have image, icons, elements, and text as a display value, whreas<input>
can only have text as value. So it become
// <button> by default have "submit" type <button id="formSubmit" class="btn_submit">Claim your free trial</button>
- If you implemented the suggestion above, then you can also refactor the inputs looping like this.
// select all input elements const inputElements = document.querySelectorAll("input"); // loop all input with forEach, where `inputEl` is the actual input element inputElements.forEach((inputEl, idx) => { inputEl.classList.remove("error"); formError[idx].style.display = "none"; //... other codes })
I hope it helps! Cheers!
1 - For Submit Button, it's better to use a
- @JaredBrown1Submitted over 1 year ago
- What did you find difficult while building the project?
What was difficult for me was getting the state in order. I had to really sit back and think about how to get all this state to load in one bigger component. Dark mode was a little struggle as well.
- Which areas of your code are you unsure of?
I am unsure of the way I handled the state of the dark mode toggle button. I want to get it to where it can toggle depending on the users system dark mode settings, if that makes sense because right now it can load in with dark mode but the button isn't toggled.
3)Do you have any questions about best practices? I would appreciate any advice on best practices regarding the way I handled the state in my application.
@fazzaamiarsoPosted over 1 year agoHi @JaredBrown1! Great work on finishing your project!
I think I can help answer your questions:
- State management is definitely hard. My recommendation is to read this article by Kent C. Dodds as a starting point. I also encourage you to read his other articles on React because I learnt a lot of best practices from him.
- In
next-themes
, You can get the user's system theme returned byuseTheme
. Here's the documentation reference. Example:
const { systemTheme } = useTheme() useEffect(() => { // set toggle state based on user's theme }, [])
- To keep your codebase clean and formatted nicely, you always want to have ESLint combined with Prettier. Here is a simple tutorial
I hope it helps! Cheers!
Marked as helpful0 - @ansman58Submitted over 1 year ago
I’d appreciate corrections on what aspects of the code I should improve on
@fazzaamiarsoPosted over 1 year agoHi @ansman58! Great job on finishing the project!
I have some suggestions for you
- Currently you are navigating by rendering component conditionally with State. I recommend you to use a routing library instead. The most popular is React Router
- In your Crew page, rather than using
useEffect
to find the selected crew, you can achieve the same result by doing it in render. Example:
// by doing this, you don't need `data` state anymore const { crewMember, setCrewMember } = React.useContext(CurrentNavContext); // this will be re-calculated everytime `crewMember` changes. const crewInfo = crew.find((item) => item.role === crewMember) ?? null; <Section subtitle={crewInfo?.role} title={crewInfo?.name as string} description={crewInfo?.bio as string} titleClass={style.sectionTitle} subtitleClass={style.sectionSubtitle} />
I hope it helps! Cheers!
0 - @andrijaivkovicSubmitted over 1 year ago
Hi! This is my solution to this challenge.
Any and all feedback is appreaciated! :)@fazzaamiarsoPosted over 1 year agoHi @andrijaivkovic! Great work!
I have some suggestion for you
Since this project require you to use an API Key which anyone can exploit if exposed to public (especially paid services). You can utilize Netlify functions in conjuction with environment variables to hide your API Key. Here are tutorials for Netlify Functions and environment variables.
I hope it helps! Cheers!
0 - @giiancarlonvSubmitted over 1 year ago
badly need help! i got this error after deploying, did everything to fix it
error message - Loading module from “https://giiancarlonv.github.io/assets/index-66f0fd8a.js” was blocked because of a disallowed MIME type (“text/html”).
*edit fixed. thanks everyone!!
@fazzaamiarsoPosted over 1 year agoHi @giiancarlonv!
I've taken a look at your code and I suspect it's because of the Script tag in
index.html
that contains the attributes below which not suppose to be theretype="module" src="https://giiancarlonv.github.io/assets/index-66f0fd8a.js"
I hope it helps! Cheers!
1 - @suhjiwon95Submitted over 1 year ago
I'm open to all suggestions! :D
@fazzaamiarsoPosted over 1 year agoHi @suhjiwon95! Great work!
I have some quick tips for you
- To improve user experience, you can fetch an initial advice so the UI doesn't feel empty. You can also insert an Empty State UI.
- When naming in Javascript, especially Frontend, it's primary convention is to use camelCase. If I'm not mistaken, snake_case is Python's convention.
const advice_id = data.slip.id; const adviceId = data.slip.id; 👍
- You can also do Object destructuring.
const { advice } = data.slip;
I hope it helps! Cheers!
Marked as helpful0