REST Countries API with color theme switcher
Design comparison
Solution retrospective
Hello, Frontend Mentor community! This is my solution to the REST Countries API with a color theme switcher.
I appreciate all the feedback you left that helped me to improve this project. I fixed the issues I had previously and added new features per your recommendation.
I have done this project a while ago however if you are here and have any feedback, tips, or ideas to share, I will be more than glad to hear them out!
Thanks
Community feedback
- @adram3l3chPosted over 2 years ago
Hi Catherine you did a great job :) . You can sort out the border issue by making the border as a component itself and fetch about that country on that component. Also the problem of github pages with SPAs can be solved by using something like
(function(l) { if (l.search[1] === '/' ) { var decoded = l.search.slice(1).split('&').map(function(s) { return s.replace(/~and~/g, '&') }).join('?'); window.history.replaceState(null, null, l.pathname.slice(0, -1) + decoded + l.hash ); } }(window.location))
You can refer about the same here
Marked as helpful2@catherineisonlinePosted over 2 years ago@adram3l3ch Thank you
0 - @fadelunPosted over 2 years ago
i have seen your website, but when dark mode some color it doesn't change. maybe you can check on style.css, there is ".region-list option"
Marked as helpful1@catherineisonlinePosted over 2 years ago@fadelun Haven't noticed before, thanks for the feedback!
0 - @denieldenPosted over 2 years ago
Hey Catherine, congratulations on completing the challenge! You did a great job π
Let me give you some little tips for optimizing your code:
- add
header
tag ad wrap thenav
to improve the Accessibility - with
main
tag wrap the content of page - add descriptive text in the
alt
attribute of the images - remove all unnecessary code, the less you write the better as well as being clearer: for example the
div
container of flag images and styling directly theimg
element - use
ul
element for the details text of country instead of multiplep
- if you want to use the title for the
href
attribute you have to parse it inurl
, it can give problems creating links with empty spaces or special characters - if I type a query that doesn't give any results, nothing happens, try adding a "no results" message
- I would also add a query reset button, I find it very convenient
- in the filters there is no way to return to all countries after choosing a region, add an entry "all region"
- in the region filter after clicking on a region the submenu does not close
- instead of using
px
use relative units of measurement likerem
-> read here - to fetch data from API create and use a custom hook so you can reuse the same code in many different components and keep the logic separate from the design
Hope this help! Happy coding π
Marked as helpful1@catherineisonlinePosted over 2 years ago@denielden Thanks for the feedback
1 - add
- @christopher-adolphePosted over 2 years ago
Hi @catherineisonline,
You did a great job in completing this challenge using React. π
- For your issue to display the border countries, I suggest you build a list of countries with their respective alpha codes on the initial load since you are already fetching all the countries. Something like this:
const countryCodes: [ { name: 'Albania', alpha2Code: 'AL', alpha3Code: 'ALB' }, { name: 'Algeria', alpha2Code: 'DZ', alpha3Code: 'DZA' } ]
Then you could store this list in a global state using React's context API and when a user goes to the
country
page you can find the country codes from thiscountryCodes
list and map them to their respective full name.- It might also be a good thing to wrap asynchronous logics in a
try...catch
block to catch any potential errors.
I hope this helps.
Keep it up.
Marked as helpful1@catherineisonlinePosted over 2 years ago@christopher-adolphe Thanks, I will take a look
0@christopher-adolphePosted over 2 years agoHi @catherineisonline, π
I'm happy to help and glad to see that this was helpful to you. π
I don't know if you had the chance to give it a try but here's what I had in mind when suggesting this.
- Create a new
context
directory under yoursrc
directory - Create a
CountriesContext.js
file inside this new directory with the following content:
import { createContext, useState } from 'react'; // Creating a context which will be accessible to // the components of the React app. Here the context // is an object with 2 keys; `onBuildCountryCodes` and // `onGetCountryNames`, which are functions to build the // country codes list and to get the country names list const CountriesContext = createContext({ onBuildCountryCodes: (countries) => {}, onGetCountryNames: (borders) => {} }); // This `CountriesContextProvider` will be use as a // wrapper component to provide the context to the // entire React app export function CountriesContextProvider({ children }) { const [ countryCodes, setCountryCodes ] = useState([]); // The `buildCountryCodesHandler` function contains the logic // to build the countryCodes list from a list of countries const buildCountryCodesHandler = (countries) => { countries.forEach(country => { const { name, alpha2Code, alpha3Code } = country; setCountryCodes(prevState => [ ...prevState, { name, alpha2Code, alpha3Code } ]); }); }; // The `getCountryNamesHandler` function contains the logic // to return a list of country name from a list of country // border codes const getCountryNamesHandler = (borders) => { return countryCodes.map(country => borders.includes(country.alpha3Code) ? country.name : null); }; // Defining a `contextValue` object that matches the structure // of the `CountriesContext` context and passing the handler // functions as value to it's keys. We will then pass it to the // `value` prop of the `<CountriesContext.Provider>` component const contextValue = { onBuildCountryCodes: buildCountryCodesHandler, onGetCountryNames: getCountryNamesHandler }; return ( <CountriesContext.Provider value={ contextValue }> { children } </CountriesContext.Provider> ); } export default CountriesContext;
- Wrap your root component inside
index.js
with the<CountriesContextProvider>
component like this:
// all your other imports import CountriesContextProvider from './context/CountriesContext'; root.render( <React.StrictMode> <CountriesContextProvider> {/* Wrapping starts here */} <BrowserRouter basename={process.env.PUBLIC_URL}> <div className="mainContainer"> <Header /> <Routes> <Route exact path="/:name" element={<Country />} /> <Route path="*" element={<Error />} /> <Route path="/" exact element={<Countries />} /> </Routes> </div> </BrowserRouter> </CountriesContextProvider> {/* Wrapping ends here */} </React.StrictMode> );
- You can now access this context in your
<Countries />
component using theuseContext
hook like this:
import { useState, useEffect, useContext } from 'react'; import { CountriesContext } from '../context/CountriesContext'; export default function Countries() { // ... // Getting the `onBuildCountryCodes` function from the context const { onBuildCountryCodes } = useContext(CountriesContext); const fetchCountries = async () => { try { setIsLoading(true); const response = await fetch(url); const data = await response.json(); setCountries(data); // Passing the list of countries fetched from the API to // the `onBuildCountryCodes` function to build the `countryCodes` list onBuildCountryCodes(data); } catch (error) { console.log(error); } finally { setIsLoading(false); } }; // ... }
- Then access the context in your
<Country />
component to get the names of the border countries like this:
import { useState, useEffect, useContext } from 'react'; import { CountriesContext } from '../context/CountriesContext'; export default function Country() { // ... const [borderGroup, setCountryBorders] = useState([]); // Getting the `onGetCountryNames` function from the context const { onGetCountryNames } = useContext(CountriesContext); useEffect(() => { const fetchCountryData = async () => { try { setIsLoading(true); const url = `https://restcountries.com/v2/name/${name}`; const response = await fetch(url); const data = await response.json(); setCountry(data); // Passing the borders list to the `onGetCountryNames` function to // get the full name of the border countries and using `setCountryBorders` // to set the `borderGroup` state setCountryBorders(onGetCountryNames(data[0].borders)); } catch (error) { console.log(error); } finally { setIsLoading(false); } }; fetchCountryData(); }, [name, borderGroup]); // ... }
Let me know if that worked for you.
Sorry, π this markdown editor doesn't render comments in code snippets nicely. Maybe you could paste them in your code editor for better readability. π
Best regards.
0 - @Javieer57Posted 10 months ago
Hi!
I found a bug in the neighboring countries' tags. When you click on one, it adds the neighboring countries of the current one to the neighboring countries from the previous screen. Excellent job! :)
1@catherineisonlinePosted 9 months ago@Javieer57 Hi, thanks for the feedback. I have fixed this issue and it should be working properly now. Thank you
0 - @JukiyoomiPosted over 2 years ago
I think you can get data of a country using its code with this link https://restcountries.com/v3.1/alpha/{code} from the api.
1@catherineisonlinePosted over 2 years ago@Jukiyoomi Thank you
0 - @qadzekPosted 9 months ago
Nice work. I like how you have implemented the "Filter by Region" menu.
A couple of small remarks:
- I can confirm the bug described by @Javieer57.
- The use of
any
in TS is discouraged. - There are tools online to automatically convert a JSON response to TS types: https://app.quicktype.io
- I don't think there is a need for separate
FilteredCountries
andAllCountries
components. - A single API call should suffice.
0@catherineisonlinePosted 9 months ago@qadzek I have fixed this issue with borders and it should be working properly now. Thanks for the feedback I will get back to the other suggestions later on
0 - @SamadeenPosted about 2 years ago
Hello Catherine... You did beautifully well on this project, can you kindly recommend resources to learn React
0@catherineisonlinePosted about 2 years ago@Samadeen Hi, thanks! I started learning by practicing and never really read docs, so if you understand JavaScript it makes React much easier. I started reading much later to understand some concepts better but the internet is full of useless information. I recommend starting with official docs: https://reactjs.org/docs/getting-started.html
It's not very hard to understand but if they have some topics not clear enough, you can google and try to find some more info.
0@catherineisonlinePosted about 2 years ago@Samadeen No worries, I hope that helps
0 - @D3press3ddPosted over 2 years ago
wow, you've grown quite a bit in the area, keep it up, check out this tool for animations https://www.framer.com/motion/ you can see what you can do in my solution to this challenge, it is really intuitive you can check this video https://www.youtube.com/watch?v=u_95SPKE6vg and see how it works in just 5 minutes
0@catherineisonlinePosted over 2 years ago@D3press3dd Thank you! Wow, this looks awesome!
0 - @sparrowslPosted over 2 years ago
This is just too amazing, makes me want to do it now. I have no words to say here :)
I only found that the spacing on the right side is not consistent with the left (on desktop) Everything else if just fire and amazing
0@catherineisonlinePosted over 2 years ago@benjithorpe I will check it out, still have to finish off the mobile version. Thanks for the feedback!
1 - @Zein-MBPosted over 2 years ago
Amazing! you're just an example of success!!
0 - @Valeri85Posted over 2 years ago
Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'value') Search.js:21 e = e.value;
0@catherineisonlinePosted over 2 years ago@Valeri85 I will check it, thanks
0 - @vickydarlinnPosted over 2 years ago
after a long time.....But Amazingπ₯
0@catherineisonlinePosted over 2 years ago@vickydarlinn Thanks!
0
Please log in to post a comment
Log in with GitHubJoin our Discord community
Join thousands of Frontend Mentor community members taking the challenges, sharing resources, helping each other, and chatting about all things front-end!
Join our Discord