Search code examples
reactjstypescriptgatsby

Uncaught TypeError: undefined is not iterable (cannot read property Symbol(Symbol.iterator)) only on production


I'm migrating a Gatsby project from JS to TS, everything works fine in development, no errors on build, but when I use gatsby serve or deploy somewhere else, I'm getting this error on browser console when I click in any button: Uncaught TypeError: undefined is not iterable (cannot read property Symbol(Symbol.iterator))

index.tsx:

interface CVData {
    name: string;
    position: string;
    address: string;
    email: string;
    website: string;
    twitter: string;
    github: string;
    skills: string[];
    languages: string[];
    education: any[];
    work: any[];
    projects: any[];
    awards: any[];
    publications: any[];
    interests: any[];
    excerpt: string;
}
const [cvdata, setCVdata] = React.useState<CVData>({
    name: "",
    position: "",
    address: "",
    email: "",
    website: "",
    twitter: "",
    github: "",
    skills: [],
    languages: [],
    education: [],
    work: [],
    projects: [],
    awards: [],
    publications: [],
    interests: [],
    excerpt: "",
  })
   React.useEffect(() => {
    setCVdata(JSON.parse(localStorage.getItem("cvdata") || '{}'))
  }, [])

  React.useEffect(() => {
    localStorage.setItem("cvdata", JSON.stringify(cvdata))
  }, [cvdata])

  const [currentLang, setCurrentLang] = React.useState("")
  const addlanguage = () => {
    const newLangs = [...cvdata.languages, currentLang] // line that throws error
    setCVdata({ ...cvdata, languages: newLangs })
    setCurrentLang("")
  }
  const remlanguage = () => {    
    const newLangs = cvdata.languages.slice(0, -1) // line that throws error
    setCVdata({ ...cvdata, languages: newLangs })
    setCurrentLang("")
  }
  
  // and the buttons:
                <button
                  type="button"
                  onClick={addlanguage}
                  className="border-2 border-blue-700 text-blue-700 font-bold p-2 hover:text-gray-300 hover:bg-blue-700 rounded-lg px-2 mb-2"
                >
                  <Trans>Add</Trans>
                </button>
                <button
                  type="button"
                  onClick={remlanguage}
                  className="border-2 border-blue-700 text-blue-700 font-bold p-2 hover:text-gray-300 hover:bg-blue-700 rounded-lg px-2 mb-2"
                >
                  <Trans>Remove</Trans>
                </button>

I'm learning TS, so I'm using lots of any, however I don't understand why it says that cvdata.languages is undefined (it also throws error for every button in the form)

You can find the full code in this branch, especifically index.tsx


Solution

  • In case there's no "cvdata" key set in localstorage, I modified the first useEffect to

    React.useEffect(() => {
        setCVdata(JSON.parse(localStorage.getItem("cvdata") || JSON.stringify(emptycvdata)))
      }, [])
    

    where emptycvdata is the default cvdata object (the one I used in useState)

    However, that was not enough, I had to check for every key of cvdata object in the onClick functions (otherwise missing keys would return undefined).

    For example, in addlanguage I replaced

    const newLangs = [...cvdata.languages, currentLang]
    

    with

    const newLangs = cvdata.languages ? [...cvdata.languages, currentLang] : [currentLang]
    

    and

    const newLangs = cvdata.languages.slice(0, -1)
    

    with

    const newLangs = cvdata.languages ? cvdata.languages.slice(0, -1) : []
    

    I did the same for the other functions and everything seems to be fine now.