Skip to content

Instantly share code, notes, and snippets.

@mabenson00
Created July 23, 2024 15:05
Show Gist options
  • Save mabenson00/180a5e4f69588e065920b08a9500fae6 to your computer and use it in GitHub Desktop.
Save mabenson00/180a5e4f69588e065920b08a9500fae6 to your computer and use it in GitHub Desktop.

DevDash Frontend Interview

Table of Contents

De-bouncing, Performance

Debouncing is a way to delay a function from executing until a certain amount of time after the last action, when a new action is performed, it resets the countdown.

Use Case: Search

import React, { useState } from "react";

// Debounce function
const debounce = (fn, delay = 1000) => {
  let timerId = null;
  return (...args) => {
    clearTimeout(timerId);
    timerId = setTimeout(() => fn(...args), delay);
  };
};

// Example function to be debounced
const makeAPICall = (value) => {
  console.log("API call with:", value);
};

const DebouncedInput = () => {
  const [inputValue, setInputValue] = useState("");

  const handleInput = debounce((value) => {
    makeAPICall(value);
  }, 500);

  const handleKeyUp = (event) => {
    const { value } = event.target;
    setInputValue(value);
    handleInput(value);
  };

  return (
    <div>
      <input type="text" value={inputValue} onKeyUp={handleKeyUp} />
    </div>
  );
};

export default DebouncedInput;

Use Case: Limiting Viewable Records on FE

Filtered Items was being displayed on the FE, and failing because it had 6,000 records. The better option is to pass a limit to the API

const filteredItems =
  searchValue === ""
    ? items.slice(0, 50)
    : items
        .filter((item) => {
          return item.name?.toLowerCase().includes(searchValue.toLowerCase());
        })
        .slice(0, 50);

Loading States

Disable Button on Load

const [loading, setLoading] = useState(false);
const uploadFiles = async () => {
  setLoading(true);
  ///await call api here
  setLoading(false);
};
<Button
  variant="primary"
  onClick={async () => await uploadFiles()}
  disabled={loading}
  className="min-w-[12rem]"
>
  {loading ? <>Uploading...</> : <>Upload</>}
</Button>;

Replacing Return Element with Loading Element

import Loading from "./loading"
import Photos from "./photos"
const PhotoContainer = () => {

  const [isLoading, setIsLoading] = useState(true);
  return (
    <div>
      {isLoading ? (
        <Loading />
      ) : (
        <div>
        <Photos>
      )}
    </div>
  );
};

User Experience

Search Query in URL

import React, { useState, useEffect } from "react";
import {
  BrowserRouter as Router,
  Route,
  useHistory,
  useLocation,
} from "react-router-dom";

// Mock function to simulate fetching search results
const fetchResults = async (query) => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve([`Result for ${query} 1`, `Result for ${query} 2`]);
    }, 1000);
  });
};

const Search = () => {
  const history = useHistory();
  const location = useLocation();
  const [query, setQuery] = useState(
    new URLSearchParams(location.search).get("query") || ""
  );
  const [results, setResults] = useState([]);
  const [isLoading, setIsLoading] = useState(false);

  // Performs search when query params (anything after the ?) change
  useEffect(() => {
    // locaction.search is react built in for anything after the ? in the url
    const params = new URLSearchParams(location.search);
    // this gets your actual query, which is set to "query="
    const searchQuery = params.get("query");
    if (searchQuery) {
      performSearch(searchQuery);
    }
    // update when location.search changes
  }, [location.search]);

  const performSearch = async (searchQuery) => {
    setIsLoading(true);
    const fetchedResults = await fetchResults(searchQuery);
    setResults(fetchedResults);
    setIsLoading(false);
  };

  const handleSearch = (e) => {
    e.preventDefault();
    // update the url location on search, which triggers use effect
    history.push(`?query=${query}`);
  };

  return (
    <div>
      <form onSubmit={handleSearch}>
        <input
          type="text"
          value={query}
          onChange={(e) => setQuery(e.target.value)}
        />
        <button type="submit">Search</button>
      </form>
      {isLoading ? (
        <p>Loading...</p>
      ) : (
        <ul>
          {results.map((result, index) => (
            <li key={index}>{result}</li>
          ))}
        </ul>
      )}
    </div>
  );
};

const App = () => (
  <Router>
    <Route path="/" component={Search} />
  </Router>
);

export default App;

Lazyload

Lazyload makes it so if you want to show 50 images at once, it will have the html already, but the images themselves won't load until you scroll down to them. This is an easy way to do pseudo infinite scroll.

(if they ask for ideas on how to make the UI better, or the experience faster, you can mention this as an easy way.)

Semantic Markup/Accessibility

  • Avoid using Divs: Header
  • Associate labels and inputs
  • Use alt tags for photos
  • aria-labelled by is a way to associate labels with any element
<body>
  <header>
    <h1>Photo Search</h1>
    <form role="search">
      <label for="search-query">Search Photos</label>
      <input type="text" id="search-query" name="query" placeholder="Enter search term" />
      <button type="submit">Search</button>
    </form>
  </header>
  <main>
    <section aria-labelledby="results-heading">
      <h2 id="results-heading">Search Results</h2>
      <ul>
        <li>
          <img src="photo1.jpg" alt="Description of photo 1" />
          <p>Photo 1 Description</p>
        </li>
        <li>
          <img src="photo2.jpg" alt="Description of photo 2" />
          <p>Photo 2 Description</p>
        </li>
        <!-- More photo results -->
      </ul>
    </section>
  </main>
  <footer>
    <p>&copy; 2024 Photo Search. All rights reserved.</p>
  </footer>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment