Skip to content

Instantly share code, notes, and snippets.

@jimode
Last active January 1, 2024 06:13
Show Gist options
  • Star 32 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save jimode/c1d2d4c1ab33ba1b7be8be8c50d64555 to your computer and use it in GitHub Desktop.
Save jimode/c1d2d4c1ab33ba1b7be8be8c50d64555 to your computer and use it in GitHub Desktop.
Providing Context and Local Storage Using Hooks
// Providing Context
// ==================
import React, {useState, useEffect} from "react"
const Context = React.createContext()
function ContextProvider({children}) {
const [allPhotos, setAllPhotos] = useState([])
const [cartItems, setCartItems] = useState([])
// Local Storage: setting & getting data
useEffect(() => {
const cartItemsData = JSON.parse(localStorage.getItem('cartItems'))
if (cartItemsData) {
setCartItems(cartItemsData)
}
}, [])
useEffect(() => {
localStorage.setItem('cartItems', JSON.stringify(cartItems))
}, [cartItems])
const url = "https://raw.githubusercontent.com/bobziroll/scrimba-react-bootcamp-images/master/images.json"
useEffect(() => {
fetch(url)
.then(res => res.json())
.then(data => setAllPhotos(data))
}, [])
function toggleFavorite(id) {
const updatedArr = allPhotos.map(photo => {
if(photo.id === id) {
return {...photo, isFavorite: !photo.isFavorite}
}
return photo
})
setAllPhotos(updatedArr)
}
function addToCart(newItem) {
setCartItems(prevItems => [...prevItems, newItem])
}
function removeFromCart(id) {
setCartItems(prevItems => prevItems.filter(item => item.id !== id))
}
return (
<Context.Provider value={{allPhotos, toggleFavorite, cartItems, addToCart, removeFromCart}}>
{children}
</Context.Provider>
)
}
export {ContextProvider, Context}
// Retrieving state using useContext
// =================================
import React, {useState, useContext} from "react"
import PropTypes from "prop-types"
import {Context} from "../Context"
function Image({className, img}) {
const [hovered, setHovered] = useState(false)
const {toggleFavorite, addToCart, cartItems, removeFromCart} = useContext(Context)
function heartIcon() {
if(img.isFavorite) {
return <i className="ri-heart-fill favorite" onClick={() => toggleFavorite(img.id)}></i>
} else if(hovered) {
return <i className="ri-heart-line favorite" onClick={() => toggleFavorite(img.id)}></i>
}
}
function cartIcon() {
const alreadyInCart = cartItems.some(item => item.id === img.id)
if(alreadyInCart) {
return <i className="ri-shopping-cart-fill cart" onClick={() => removeFromCart(img.id)}></i>
} else if(hovered) {
return <i className="ri-add-circle-line cart" onClick={() => addToCart(img)}></i>
}
}
return (
<div
className={`${className} image-container`}
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
>
<img src={img.url} className="image-grid"/>
{heartIcon()}
{cartIcon()}
</div>
)
}
Image.propTypes = {
className: PropTypes.string,
img: PropTypes.shape({
id: PropTypes.string.isRequired,
url: PropTypes.string.isRequired,
isFavorite: PropTypes.bool
})
}
export default Image
@boriskondev
Copy link

This is pure gold! I want banging my head on the wall looking Context + localStorage solution for so long and this snippet saved my day and sanity :D Thank you!

@sam-frampton
Copy link

Nice one!

@avoajaugochukwu
Copy link

Thank you.

@jimode
Copy link
Author

jimode commented Feb 11, 2022

My pleasure @boriskondev , @sam-frampton & @avoajaugochukwu . I'm super glad it was useful for you all.

@DrMint
Copy link

DrMint commented Feb 22, 2022

Agreed, this was super simple! Thanks a bunch! I was struggling with this hook from usehooks-ts (useLocalStorage), but I ended up just remaking a simpler one using your code:

import { useEffect, useState } from "react";

export default function useStateWithLocalStorage<T>(
  key: string,
  initialValue: T
): [T, React.Dispatch<React.SetStateAction<T>>] {
  const [value, setValue] = useState<T>(initialValue);

  useEffect(() => {
    try {
      const item = localStorage.getItem(key);
      if (item) setValue(JSON.parse(item) as T);
    } catch (error) {
      console.warn(`Error reading localStorage key “${key}”:`, error);
    }
  }, [setValue, key]);

  useEffect(() => {
    localStorage.setItem(key, JSON.stringify(value));
  }, [value, key]);

  return [value, setValue];
}

Then I use it in my context provider like so:

const [darkMode, setDarkMode] = useStateWithLocalStorage<boolean>("darkMode", false);

I've only tested it for storing/retrieving boolean values, so it's possible it needs to be adapted a bit for other types.

@Makashov
Copy link

Makashov commented Sep 6, 2022

Thank you!

@arudovwen
Copy link

Thanks a lot, was battling for a solution for days now

@B-Schmitz
Copy link

How do i use this, but without passing a fixed value to my key

I need to access the storage inside the useEffect, but my getItem doesn't receive a fixed string key

@Leen-odeh3
Copy link

Nice one!, Thanks a lot

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment