Skip to content

Latest commit

 

History

History
286 lines (227 loc) · 7.27 KB

README.md

File metadata and controls

286 lines (227 loc) · 7.27 KB

Frontend Mentor - REST Countries API with color theme switcher solution

This is a solution to the REST Countries API with color theme switcher challenge on Frontend Mentor. Frontend Mentor challenges help you improve your coding skills by building realistic projects.

Table of contents

Overview

The challenge

Users should be able to:

  • See all countries from the API on the homepage
  • Search for a country using an input field
  • Filter countries by region
  • Click on a country to see more detailed information on a separate page
  • Click through to the border countries on the detail page
  • Toggle the color scheme between light and dark mode

Screenshot

gif

Links

My process

Built with

  • Semantic HTML5 markup
  • CSS custom properties
  • Flexbox
  • CSS Grid
  • Desktop-first workflow
  • React - JS library
  • Redux Toolkit - State Manager
  • React Router - For Routes
  • Axios - For HTTP requests
  • Sass - CSS pre-processor

What I learned

I've learned lot of things in this challenge:

  • How to use Redux toolkit for state management
// store/theme.js
import { createSlice } from '@reduxjs/toolkit';

const themeSlice = createSlice({
  name: 'theme',
  initialState: {
    isDark: window.matchMedia('(prefers-color-scheme: dark)').matches,
  },
  reducers: {
    toggle(state) {
      state.isDark = !state.isDark;
      document.querySelector('body').classList.toggle('dark-theme');
    },
  },
});

const { reducer: themeReducer, actions: themeActions } = themeSlice;
export { themeReducer as default, themeActions };
// store/index.js
import { configureStore } from '@reduxjs/toolkit';
import themeReducer from './theme';

const store = configureStore({
  reducer: {
    theme: themeReducer,
    filterRegion: filterRegionReducer,
    countries: countriesReducer,
    search: searchReducer,
  },
});

export default store;
// index.js
import { Provider } from 'react-redux';
import store from './store';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <Provider store={store}>
    <App />
  </Provider>
);
// Uses | components/SearchBar
import { useDispatch, useSelector } from 'react-redux';
import { countriesActions } from '../../store/countries';
import { searchActions } from '../../store/search';

const SearchBar = () => {
  const isDark = useSelector(state => state.theme.isDark);
  const search = useSelector(state => state.search.value);
  const { searchCountry } = countriesActions;
  const { setSearch } = searchActions;
  const dispatch = useDispatch();

  const searchChangeHandler = e => {
    dispatch(setSearch(e.target.value));

    if (e.target.value.trim() === '') {
      return;
    }

    dispatch(searchCountry(e.target.value.trim()));
  };

  return (
    <Card className="search-bar">
      {isDark || <ion-icon name="search-sharp" />}
      {isDark && <ion-icon name="search-sharp" className="dark" />}
      <input
        className="search-bar__input"
        type="text"
        name="search"
        placeholder="Search for a country..."
        value={search}
        onChange={searchChangeHandler}
      />
    </Card>
  );
};
  • How to work with custom hooks
// hooks/use-https
import axios from 'axios';
import { useCallback, useState } from 'react';

const useHttp = () => {
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState(null);

  const sendRequest = useCallback(async (url, callback) => {
    setIsLoading(true);
    setError(null);

    try {
      const res = await axios.get(url);
      callback(res.data);
    } catch (err) {
      setError({ ...err, message: err.message || 'Something went wrong!' });
    }

    setIsLoading(false);
  }, []);

  return { isLoading, error, sendRequest };
};
  • How to use react router
const App = () => {
  return (
    <div className="App">
      <Header />
      <Routes>
        <Route path="/" element={<AllCountries />} />
        <Route path=":countryId" element={<Country />} />
        <Route path="*" element={<NotFound />} />
      </Routes>
      <Footer />
    </div>
  );
};
  • How to use lazy loading
import React, { Suspense } from 'react';

// Loaded only when these routes are visited
const NotFound = React.lazy(() => import('./pages/NotFound'));

const App = () => {
  return (
    <div className="App">
      <Header />
      <Suspense fallback={<Spinner />}>
        <Routes>
          <Route path="*" element={<NotFound />} />
        </Routes>
      </Suspense>
      <Footer />
    </div>
  );
};
  • How to make dark themes in React with Sass
@mixin dark-theme {
  --color-bg: #202c37;
  --color-primary: #2b3945;
  --color-grey: #cbd5e1;
  --color-text: #ffffff;
}

@mixin light-theme {
  --color-bg: #fafafa;
  --color-primary: #ffffff;
  --color-grey: #64748b;
  --color-text: #111517;
}

:root {
  // Theme
  @include light-theme;
}

@media (prefers-color-scheme: dark) {
  :root {
    @include dark-theme;
  }

  .dark-theme {
    @include light-theme;
  }
}

Continued development

Technologies I'd be learning soon:

  • NextJs
  • Typescript
  • Testing (JS)
  • Blockchain Development
  • Flutter & Dart
  • Cyber Security

Useful resources

  • MDN Docs - This is an amazing reference which helped me finally understand detailed concepts like data- attr, aria attr, input range etc.
  • W3Schools - This is an amazing website for learning, I've learned about semantic tags from here only and learned many important HTML elements. I'd recommend it to anyone still learning this concept.
  • React Router Docs - Best reference to get start with React Router

Author

Acknowledgments