Also, when a user logs in, I retrieve user’s data from firebase and store in the movie data state for rendering.
import React, { useState, useContext, useEffect } from 'react'
import { app, database } from '../firebaseConfig'
import { doc, setDoc } from 'firebase/firestore'
import data from 'StarterAssets/data.json'
//Toastify package
import { toast } from 'react-toastify'
//Get Data stored in local storage
const getLocalStorage = () => {
let movieData = localStorage.getItem('movieData')
if (movieData) {
return JSON.parse(localStorage.getItem('movieData'))
} else {
return data
}
}
const AppContext = React.createContext()
const AppProvider = ({ children }) => {
const [userID, setUserID] = useState('')
const [movieData, setMovieData] = useState(getLocalStorage())
//const [movieData, setMovieData] = useState(data)
const [searchValue, setSearchValue] = useState('')
useEffect(() => {
localStorage.setItem('movieData', JSON.stringify(movieData))
}, [movieData])
//loops over an array, compares the ids of passed and contained and changes the value of bookmarked prop
const bookmarkMovie = (id) => {
const newState = movieData.map((obj) => {
// if id equals obj[id], update bookmarked property
if (obj.id === id) {
const { isBookmarked } = obj
return { ...obj, isBookmarked: !isBookmarked }
}
// else return the object as it is
return obj
})
setMovieData(newState)
}
console.log(movieData)
const searching = {
home: 'Search for movies or TV series',
tv: 'TV series',
movies: 'movies',
bookmarks: 'bookmarked shows',
}
useEffect(() => {
const sendData = async () => {
await setDoc(doc(database, 'users', userID), { movieData })
}
sendData().catch((error) => toast.error(error))
}, [movieData])
return (
<AppContext.Provider
value={{
userID,
setUserID,
searching,
setMovieData,
movieData,
bookmarkMovie,
searchValue,
setSearchValue,
}}
>
{children}
</AppContext.Provider>
)
}
export const useGlobalContext = () => {
return useContext(AppContext)
}
export { AppContext, AppProvider }
Here’s my Home.jsx file where the error(Type error: Cannot read property of (filter) in home.jsx is picked up from the console. I omitted the import statements due to limits for posts.
import React from 'react'
import styles from 'Pages/Home/home.module.scss'
import { useGlobalContext } from 'Hooks/context'
import Show from 'Components/Show'
import SearchResults from 'Components/SearchResults'
//Abstract Data // Icons
import IconPlay from 'StarterAssets/assets/icon-play.svg'
import iconBookmarkEmpty from 'StarterAssets/assets/icon-bookmark-empty.svg'
import iconBookmarkFull from 'StarterAssets/assets/icon-bookmark-full.svg'
const Home = () => {
const { movieData, bookmarkMovie, searchValue } = useGlobalContext()
//Array of recommended movie section
const recommendedMovies = movieData.filter((movie) => {
return movie.thumbnail?.regular && !movie.isTrending
})
//Display search results
if (searchValue) return <SearchResults movieArray={movieData} />
//Return the entire array if the search value is empty... More like the default component
return (
<section className={styles.main}>
<div className={styles.trending}>
<h1 className='heading-large'>Trending</h1>
<div className={`${styles.trend__grid} ${styles.snap__inline}`}>
{movieData
.filter((movie) => {
return movie.thumbnail?.trending
})
.map(
({
title,
id,
thumbnail: { trending },
year,
category,
rating,
isBookmarked,
}) => {
return (
<div key={id} className={`${styles.trend} packed`}>
<picture className={styles.wrapper__image}>
<source
media='(min-width: 801px)'
srcSet={trending.large}
/>
<img
src={trending.small}
alt='Top Trending movie'
className={styles.trend__img}
/>
</picture>
<div className={styles.icon__wrapper}>
<img src={IconPlay} alt='click to play movie' />
<p>Play</p>
</div>
<div
className={styles.bookmark__wrapper}
onClick={() => bookmarkMovie(id)}
>
<img
src={
isBookmarked ? iconBookmarkFull : iconBookmarkEmpty
}
alt='click to bookmark'
/>
</div>
<div
className={`${styles.category__wrapper} ${styles.category__align}`}
>
<div className={styles.types}>
<p className={styles.year}>{year}</p>
<span></span>
<p className={styles.type}>{category}</p>
<span></span>
<p className={styles.rating}>{rating}</p>
</div>
<h2 className={styles.subheading}>{title}</h2>
</div>
</div>
)
}
)}
</div>
</div>
<div className={styles.recommended}>
<h1 className='heading-large'>Recommended For You</h1>
<div className={styles.recommend__grid}>
{recommendedMovies.map((movie) => {
return (
<Show key={movie.id} {...movie} bookmarkMovie={bookmarkMovie} />
)
})}
</div>
</div>
</section>
)
}
export default Home
My login functionality is situated here in the login.jsx:
onSubmit: async (values, { resetForm }) => {
//Submitting user's details for verification
let oldUser
try {
oldUser = await signInWithEmailAndPassword(
auth,
formik.values.email,
formik.values.password
)
toast.success('Logged In Successfully')
resetForm()
navigate('/')
} catch (error) {
toast.error('Wrong Details / check your network')
}
const { user } = oldUser
setUserID(user.uid)
//Getting logged in user's data from firebase
const docRef = doc(database, 'users', user.uid)
const docSnap = await getDoc(docRef)
//Storing said data into state via context API
const { data } = docSnap.data()
//setMovieData(docSnap.data())
setMovieData(data)
},
})
Here’s the link to the site: entertainment-mini-app.netlify.app A demo login details email : qwe@gmail.com Password: 123456
I will appreciate all the help I can get in order to feel I’ve fully achieved something today… Cause I’ve been on this bug for longer than I thought. I opted for local storage because I wanted to persist state when the page is refreshed. because I’m altering the property of isbookmarked whenever the bookmarked icon is clicked.