Halp_am_stuck
Halp_am_stuck

Reputation: 207

Store number using localStorage with custom hook React

I'm trying to store a number for a counter that initializes as 0 and counts up 1 every time a user uploads a photo. Im getting undefined in Profile console.log orderHook.orderCount. In localStorage i get console.log of {count:0}
Any help is greatly appreciated!

LocalStorage.js:

import { useState, useEffect } from 'react';

function getStorageValue(key, defaultValue) {
    // getting stored value
    const saved = localStorage.getItem(key);
    const initial = JSON.parse(saved); // unexpected character @ line 1 column 1 
    // const initial = JSON.stringify(saved); 
    return initial || defaultValue;
}

export const useLocalStorage = (key, defaultValue) => {
    const [value, setValue] = useState(() => {
        console.log(key)
        return getStorageValue(key, defaultValue);
    });

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

    return [value, setValue];
};

Count.js: custom hook

import { useLocalStorage } from "../../Utilities/localStorage/localStorage"; // Local storage hook

function useOrderCountHook() {
    const [orderCount, setOrderCount] = useLocalStorage({count: 0}); // The profile image

    const changeOrderCount = () => {
        setOrderCount({ count: orderCount.count + 1 })
    }
    return { orderCount, changeOrderCount };
    // return [orderCount, changeOrderCount];
}

export default useOrderCountHook;

Profile.js: using customhook

    const orderHook = useOrderCountHook(); // How the photo count hook is called

    console.log(typeof (orderHook.orderCount)) // undefined
    console.log(orderHook.orderCount) // undefined

    const handleUploadChange = e => { // Input onChange 
        if (e.target.files[0]) {
            setImage(e.target.files[0]);
        } else { }
        return handleCountChange()
    };

    const handleCountChange = () => { // Function for custom count hook
        if (orderHook.orderCount === undefined) {
            return 
        } else {
            return orderHook.orderCount
        }
    }
      return (
                        {
                            currentUser ?
                                <input
                                    type="file"
                                    for="Upload Image"
                                    accept="image/*"
                                    name="image"
                                    id="file"
                                    onChange={handleUploadChange}
                                    onClick={handleUploadChange}
                                    style={{ display: "none" }}
                                />
                                : ''
                           }

                        <i className="bi bi-camera">      
                            {orderHook.orderCount === undefined ?
                                <span className="banner-list-font mx-1">0 photos</span>
                                :
                                <span className="banner-list-font mx-1">{orderHook.orderCount.count} photos</span>
                            }
                        </i>

**UPDATE**
So i managed to get it working but when i signed the user out and signed back in now if i try to upload a photo i get the error Cannot read properties of undefined (reading 'count'). which is the defaultValue prop and that console logs as undefined. I think the problem is that count is being stored as an object ?

localStorage.js:

import { useState, useEffect } from 'react';

// function getStorageValue(key, defaultValue) {
//     const saved = localStorage.getItem(key);
//     console.log(saved, defaultValue, key)// => null undefined {count:0}
//     const initial = JSON.parse(saved);
//     console.log(initial)
//     // const initial = JSON.stringify(saved); 
//     // return key || defaultValue;
//     return initial || defaultValue;
// }

function getStorageValue(key, defaultValue) {
    const saved = localStorage.getItem(key);
    console.log(saved, defaultValue, key)// => null undefined {count:0}
    if (saved === null) {
        return defaultValue;
    }
    return JSON.parse(saved);
}

export const useLocalStorage = (key, defaultValue) => {
    console.log(key, defaultValue)// => {count:0} undefined
    const [value, setValue] = useState(() => {
        return getStorageValue(key, defaultValue);
    });

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

    return [value, setValue];
};

Profile.js:
return (
<input
      type="file"
      for="Upload Image"
      accept="image/*"
      name="image"
      id="file"
      onChange={e => { orderHook.changeOrderCount(e); handleUploadChange(e) }}
      onClick={handleUploadChange}
      style={{ display: "none" }}
     />

 <i className="bi bi-camera"></i>                              
    {orderHook.orderCount === undefined || orderHook.orderCount === null ?
     <span className="banner-list-font mx-1">0 photos</span> 
     :
     <span className="banner-list-font mx-1">{orderHook.orderCount.count} photos</span>
     }

Upvotes: 1

Views: 1059

Answers (3)

Halp_am_stuck
Halp_am_stuck

Reputation: 207

The majority of the changes are in localStorage.js, first i get the key from Count.js and save it to a var saved that outputs the current count as '{"count":5}'. Next i create a var currentVal (previously named initial) and JSON.parse the passed in saved var. JSON.parse converts the string object back into a regular javascript object. resulting in a value now of {count: 5} (or whatever current count: is). Then if the expected object with a count {count: 5} (currentVal) is returning undefined or null give the key which would be {count: 0} else return currentVal. Key is acting as the defaultValue in this instance, not sure why. Also not sure how reusable this localStorage Hook would really be but it should work with any formats similar to Count.js.

Also note the new input onChange handlers and the new conditional rendering the span'(s) below it.

LocalStorage.js:

import { useState, useEffect } from 'react';

function getStorageValue(key, defaultValue) {
    const saved = localStorage.getItem(key);
    console.log(saved, defaultValue, key)
    const initial = JSON.parse(saved); // unexpected character @ line 1 column 1 
    console.log(initial)
    if (initial === null || initial === undefined) {
        return key
    } else {
        return initial
    }
}

export const useLocalStorage = (key, defaultValue) => {
    console.log(key, defaultValue)
    const [value, setValue] = useState(() => {
        return getStorageValue(key, defaultValue);
    });

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

    return [value, setValue];
};

Count.js:

import { useLocalStorage } from "../../Utilities/localStorage/localStorage"; // Local storage hook

function useOrderCountHook() {
    const [orderCount, setOrderCount] = useLocalStorage({count: 0}); // The profile image

    const changeOrderCount = () => {
        setOrderCount({ count: orderCount.count + 1 })
    }
    
    return { orderCount, changeOrderCount };
}

export default useOrderCountHook;

Profile.js:

const orderHook = useOrderCountHook(); // Photo count hook
                {
                    currentUser ?
                        <input
                            type="file"
                            for="Upload Image"
                            accept="image/*"
                            name="image"
                            id="file"
                            onChange={e => { orderHook.changeOrderCount(e); handleUploadChange(e) }}
                            onClick={handleUploadChange}
                            style={{ display: "none" }}
                        />
                        : ''
                }

                 <div className="">
                    <i className="bi bi-camera"></i>                              
                    {orderHook.orderCount === undefined || orderHook.orderCount === null ?
                    <span className="banner-list-font mx-1">0 photos</span> 
                     :
                     <span className="banner-list-font mx-1">{orderHook.orderCount.count} photos</span>                    
                  </div>

Upvotes: 1

Marius
Marius

Reputation: 90

Instead of:

    const [orderCount, setOrderCount] = useLocalStorage({count: 0}); // The profile image

you should use:

    const [orderCount, setOrderCount] = useLocalStorage("count", 0); // The profile image

Upvotes: 1

webNeat
webNeat

Reputation: 2828

It looks like the error comes from your getStorageValue function, here is a fixed implementation:

function getStorageValue(key, defaultValue) {
  const saved = localStorage.getItem(key);
  if (saved === null) {
    return defaultValue;
  }
  return JSON.parse(saved);
}

I would recommend using a library instead of writing your own implementation of useLocalStorage. Check out the useStorage hook from the react-tidy library.

Disclaimer I am the author of that library.

Upvotes: 1

Related Questions