Antonio Pavicevac-Ortiz
Antonio Pavicevac-Ortiz

Reputation: 7739

Persistence with localStorage with useState and useContext React Hooks

I have set up a Provider to share some state for a user using useContext. As such I am trying to leverage local storage to save some state for a users images (avatars, etc.)

To start I'm tryingto persist the avatar for a user, essentially saving their ID from express and then using it when I make a call to Cloudinary (an image hosting service).

I think I'm close (as I get the default image-placeholder for the avatar to work) but I can't set the local storage.

import React, { useState, useEffect } from 'react';
import dynamic from 'next/dynamic';

var initialState = {
  userId: null,
  avatar: '/static/uploads/profile-avatars/placeholder.jpg'
};

var UserContext = React.createContext();

function getLocalStorage() {
  return Object.keys(initialState).forEach(item => {
    dynamic(
      () =>
        Object.keys(initialState).forEach(
          val => (initialState[window.localStorage.getItem(item)] = val)
        ),
      {
        ssr: false
      }
    );
  });
}

function setLocalStorage() {
  Object.keys(initialState).forEach(item => {
    console.log('item setLocalStorage', item);
    dynamic(
      () =>
        window.localStorage.setItem(
          item,
          Object.values(initialState).forEach(item => item)
        ),
      {
        ssr: false
      }
    );
  });
}

function UserProvider({ children }) {
  var [userImages, setUserImages] = useState(() => getLocalStorage() || initialState);

  var [userId, setUserId] = useState(userImages.userId);

  useEffect(() => {
    setLocalStorage();
  }, [userId]);

  return (
    <UserContext.Provider
      value={{
        userImages,
        setUserImages,
        userId,
        setUserId
      }}
    >
      {children}
    </UserContext.Provider>
  );
}

export default UserContext;

export { UserProvider };

Thank you in advance, Happy Father's day!

Upvotes: 11

Views: 13952

Answers (1)

hangindev.com
hangindev.com

Reputation: 4873

I believe next/dynamic is not needed in this scenario. ES2020 dynamic import() is for importing JavaScript modules (inc. React Components) dynamically. Here you are not importing anything.

Also, keys and values in localStorage are always in string format. That's why you may skip null value(or it will be converted to string "null"). I'd suggest you to store the whole user state as an object by using JSON.stringify.

So the setLocalStorage() and getLocalStorage() will be:

function setLocalStorage(key, value) {
  try {
    window.localStorage.setItem(key, JSON.stringify(value));
  } catch (e) {
    // catch possible errors:
    // https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API
  }
}

function getLocalStorage(key, initialValue) {
  try {
    const value = window.localStorage.getItem(key);
    return value ? JSON.parse(value) : initialValue;
  } catch (e) {
    // if error, return initial value
    return initialValue;
  }
}

Then, you may use them in the UserProvider as followed:

function UserProvider({ children }) {
  const [user, setUser] = useState(() => getLocalStorage("user", initialState));

  useEffect(() => {
    setLocalStorage("user", user);
  }, [user]);

  return (
    <UserContext.Provider
      value={{
        userId: user.id,
        userAvatar: user.avatar,
        userImages: user.images,
        setUserId: (id) => setUser({ ...user, id }),
        setUserAvatar: (avatar) => setUser({ ...user, avatar }),
        setUserImages: (images) => setUser({ ...user, images }),
      }}
    >
      {children}
    </UserContext.Provider>
  );
}

Upvotes: 18

Related Questions