diff --git a/src/components/Navbar.js b/src/components/Navbar.js index 3efef59..509c4d2 100644 --- a/src/components/Navbar.js +++ b/src/components/Navbar.js @@ -1,108 +1,94 @@ -import React, { Component } from 'react'; -import { NavLink } from 'react-router-dom'; -import _ from 'lodash'; -import { withRouter } from 'react-router-dom'; +import React, { useState, useEffect } from 'react' +import { NavLink } from 'react-router-dom' +import _ from 'lodash' +import { withRouter } from 'react-router-dom' -import axios from '../axios-movies'; -import SearchLogo from '../static/images/search-icon.svg'; -import NetflixLogo from '../static/images/Netflix_Logo_RGB.png'; -import BellLogo from '../static/images/bell-logo.svg'; -import DropdownArrow from '../static/images/drop-down-arrow.svg'; -import DropdownContent from "../components/DropdownContent"; +import { useScroll } from '../hooks/useScroll' +import { useDebounce } from '../hooks/useDebounce' +import axios from '../axios-movies' +import SearchLogo from '../static/images/search-icon.svg' +import NetflixLogo from '../static/images/Netflix_Logo_RGB.png' +import BellLogo from '../static/images/bell-logo.svg' +import DropdownArrow from '../static/images/drop-down-arrow.svg' +import DropdownContent from '../components/DropdownContent' -class Navbar extends Component { - constructor(props) { - super(props) - this.state = { - scrolling: false, - userInput: '' +const Navbar = (props) => { + const [userInput, setUserInput] = useState('') + const [scrollDimensions] = useScroll() + const { scrollY } = scrollDimensions + + const debouncedUserInput = useDebounce(userInput, 500) + + const onChange = async (event) => { + setUserInput(event.target.value) + } + + useEffect(() => { + onSearchUserInputHandler(userInput) + if (debouncedUserInput) { + onSearchUserInputHandler(debouncedUserInput) } - // use to debounce api call - this.makeAipCall = _.debounce(this.makeAipCall, 1000) - } + }, [debouncedUserInput]) - componentDidMount() { - window.addEventListener('scroll', this.handleScroll); - } - - componentWillUnmount() { - window.removeEventListener('scroll', this.handleScroll); - } - - /** changes the scrolling state depending on the Y-position */ - handleScroll = () => { - if (window.scrollY === 0) { - this.setState({ scrolling: false }); - } - else if (window.scrollY > 50) { - this.setState({ scrolling: true }); - } - } - - onChange = async (event) => { - await this.setState({ userInput: event.target.value }) - const { userInput } = this.state - - await this.makeAipCall(userInput); - } - - /** Make API call as soon as the user starts typing. */ - makeAipCall = async (searchItem) => { + const onSearchUserInputHandler = async (searchItem) => { if (searchItem.length === 0) { - this.props.history.push('/') + props.history.push('/') return } - const url = `/search/multi?api_key=${process.env.API_KEY}&language=en-US&include_adult=false&query=${searchItem}`; - const response = await axios.get(url); - const results = response.data.results; - this.props.history.push({ + const url = `/search/multi?api_key=${process.env.API_KEY}&language=en-US&include_adult=false&query=${searchItem}` + const response = await axios.get(url) + const results = response.data.results + console.log('called api...') + props.history.push({ pathname: '/search', movieRows: results, - userInput: searchItem - }); + userInput: searchItem, + }) } - onLogoClick = () => { - // reset input state - this.setState({ userInput: '' }) + const onLogoClick = () => { + setUserInput('') } - render() { - const { scrolling } = this.state; + return ( + + ) } -export default withRouter(Navbar); +export default withRouter(Navbar) diff --git a/src/hooks/useDebounce.js b/src/hooks/useDebounce.js new file mode 100644 index 0000000..23ad049 --- /dev/null +++ b/src/hooks/useDebounce.js @@ -0,0 +1,25 @@ +import { useState, useEffect } from 'react' + +export const useDebounce = (value, delay) => { + // State and setters for debounced value + const [debouncedValue, setDebouncedValue] = useState(value) + + useEffect( + () => { + // Update debounced value after delay + const handler = setTimeout(() => { + setDebouncedValue(value) + }, delay) + + // Cancel the timeout if value changes (also on delay change or unmount) + // This is how we prevent debounced value from updating if value is changed ... + // .. within the delay period. Timeout gets cleared and restarted. + return () => { + clearTimeout(handler) + } + }, + [value, delay] // Only re-call effect if value or delay changes + ) + + return debouncedValue +} diff --git a/src/hooks/useScroll.js b/src/hooks/useScroll.js new file mode 100644 index 0000000..05a8aa5 --- /dev/null +++ b/src/hooks/useScroll.js @@ -0,0 +1,26 @@ +import { useState, useEffect } from 'react' + +const scrollY = window.scrollY || document.documentElement.scrollTop +const scrollX = window.scrollY || document.documentElement.scrollWidth + +export const useScroll = () => { + const [scrollDimensions, setScrollDimensions] = useState({ scrollY, scrollX }) + + const deriveScrollDimensions = () => { + const scrollY = window.scrollY || document.documentElement.scrollTop + const scrollX = window.scrollY || document.documentElement.scrollWidth + + setScrollDimensions({ scrollY, scrollX }) + } + + useEffect(() => { + deriveScrollDimensions() + window.addEventListener('scroll', deriveScrollDimensions) + + return () => { + window.removeEventListener('resize', deriveScrollDimensions) + } + }, []) + + return [scrollDimensions] +} diff --git a/src/pages/NotFound.js b/src/pages/NotFound.js index 79579e8..695e077 100644 --- a/src/pages/NotFound.js +++ b/src/pages/NotFound.js @@ -1,9 +1,11 @@ import React from 'react'; -export default function NotFound() { +const NotFound = () => { return (