Search code examples
javascriptreactjswebcomponentstypeerror

TypeError: list.map not a function


I'm building an app for React Practice and I'm getting an error while trying to store an array of objects to local storage. I'm a beginner at React so I'm not sure what's going on. I get the error in the IncomeOutputList component, because I am trying to list a bunch of objects using an array.

Here is my code:

App component:

import React, { useState, useReducer } from 'react';
   import BudgetInput from './components/input/BudgetInput';
   import IncomeOutputList from './components/output/IncomeOutputList';
   import IncomeOutput from './components/output/IncomeOutput';

const useSemiPersistentState = (key, initialState) => {
  const [value, setValue] = React.useState(
    localStorage.getItem(key) || initialState
  );
  
  React.useEffect(()=>{
    localStorage.setItem(key, JSON.stringify(value));
  }, [value, key])

  return [value, setValue];
};

const App = () => {
  const [incomes, setIncomes] = useSemiPersistentState('income',[{}]);
  const [description, setDescription] = useState('');
  const [type, setType] = useState('+');
  const [value, setValue] = useState('');

  const incomeObj = {
    desc: description,
    budgetType: type,
    incomeValue: value
  }

  const handleIncomeObjArray = () => {
    setIncomes(incomes.concat(incomeObj));
    console.log(incomes + "testing");
  }

  const handleChange = (event) => {  //this handler is called in the child component BudgetInput
    setDescription(event.target.value);
  }

  const handleSelectChange = (event) => {  //this handler is called in the child component BudgetInput
    setType(event.target.value);
  }

  const handleValueChange = (event) => {
    setValue(event.target.value);
    console.log(incomeObj)
  }

  return (
    <div className="App">

      <div className="top">

        
      </div>

      <div className="bottom">
        <BudgetInput 
          descValue={description}
          onDescChange={handleChange}
          onSelectChange={handleSelectChange}
          type={type}
          onBudgetSubmit={handleIncomeObjArray}
          budgetValue={value}
          onValChange={handleValueChange}
        />

        {/* <IncomeOutput 
          obj={incomeObj}
        /> */}

        {/* <IncomeOutput 
          desc={description}
          type={type}
        /> */}

        <IncomeOutputList 
          list={incomes}
        /> 
      </div>

    </div>
  )
};

IncomeOutputList component:

import React from 'react';
import IncomeOutput from './IncomeOutput';


// list will be list of income objects
const IncomeOutputList = ({ list }) => {
    return (
        <div>
            {list.map(item => <IncomeOutput 
                                id={item.id} 
                                value={item.incomeValue} 
                                type={item.budgetType} 
                                desc={item.desc}  
                              />)}
        </div>
    )
}

export default IncomeOutputList;

IncomeOutput component:

import React from 'react';
import ValueOutput from './ValueOutput';

const IncomeOutput = ({ desc, type,id, value }) => {
    //id = inc-{id}
        return (
            <>
                <div className="item clearfix" id={id}>
                    <div className="item__description">{desc}</div>
                        <ValueOutput
                            type={type}
                            value={value}
                        />
                </div>
            </>
        )
    }

export default IncomeOutput;

EDIT: here is a codesandbox for this code: https://codesandbox.io/s/busy-shirley-bwhv2?file=/src/App.js:2394-2509


Solution

  • It's because your Custom reducer return a string and not an array

    try this

    const useSemiPersistentState = (key, initialState) => {
      const [value, setValue] = React.useState(
        localStorage.getItem(key) ? JSON.parse(localStorage.getItem(key)) : initialState
      );
      
      React.useEffect(()=>{
        localStorage.setItem(key, JSON.stringify(value));
      }, [value, key])
    
      return [value, setValue];
    };