简体   繁体   中英

React list.map is not a function when deleting from local storage

I'm working on a budget tracking app, where the user can add different incomes and expenses. I'm using useReducer to manage state. Each income and expense is an object and when the user submits an income or expense, it is displayed as an income list with income items or expense list with expense items. Each item in the list has a remove button that should remove the income item or expense item. But when I try click the remove button to remove an income item for example, I get an error saying list.map is not a function.

Not exactly sure why this happens, but I think it has something to do with the removeItem function in IncomeOutputList.js. What would be the proper way to remove an income item?

App.js

import React, { useState, useReducer } from 'react';
import './App.css';
import BudgetInput from './components/input/BudgetInput';
import BudgetOutput from './components/output/BudgetOutput';
import IncomeOutputList from './components/output/IncomeOutputList';
import ExpenseOutputList from './components/output/ExpenseOutputList';

const useSemiPersistentState = (key, initialState) => {
 // console.log(JSON.parse(localStorage.getItem(key)));
  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];
};

const initialBudget = {
  description: '',
  type: '+',
  key: 'income',
  value: ''
};

const initialState = {
  incomes: [{}],
  expenses: [{}],
  budgetObj: initialBudget
};

const budgetReducer = (state, action) => {
  switch(action.type) {
    case "CHECK_STATE":
      console.log(state); //just to check state on submit
    case "ON_DESC_CHANGE":
      return {
        ...state,
        budgetObj: {
          ...state.budgetObj,
          description: action.payload
        }
      }
    case "ON_TYPE_CHANGE":
      const isExpense = action.payload === '-';
      return {
        ...state,
        budgetObj: {
          ...state.budgetObj,
          type: action.payload,
          key: isExpense ? 'expense' : 'income'
        }
      }
    case "ON_VALUE_CHANGE":
      return {
        ...state,
        budgetObj: {
          ...state.budgetObj,
          value: action.payload, 
        }
      }
    case 'SUBMIT_BUDGET':
      console.log(state.incomes);
      const budget = {...state};
      const isIncome = budget.budgetObj.type === '+';
      return {
        incomes: isIncome ? state.incomes.concat(budget.budgetObj) : state.incomes, 
        expenses: isIncome ? state.expenses : state.expenses.concat(budget.budgetObj), 
        budgetObj: initialBudget,  
      }
    case "REMOVE_ITEM":
      console.log('test');
      return {
        ...state,
        // not sure if the following line is the correct way to remove from local storage
        incomes: (index) => JSON.parse(localStorage.getItem("income")).splice(index,1),
       // expenses: (index) => JSON.parse(localStorage.getItem("expense")).splice(index,1)
      }
    default:
      return state;
  }
}

const useSemiPersistantReducer = (key, initialState) => {
  const [value, dispatch] = React.useReducer(
    budgetReducer,
    localStorage.getItem(key) ? JSON.parse(localStorage.getItem(key)) : initialState
  );

  React.useEffect(()=>{
    localStorage.setItem(key, JSON.stringify(value));
  }, [value, dispatch]) //[inocmes, setIncomes]

  return [value, dispatch];
}


const App = () => {


 const [budgetState, setBudget] = useSemiPersistantReducer(initialState.budgetObj.key,initialState);

 const {incomes, expenses, budgetObj, key} = budgetState;

  return (
    <div className="App">
<link href="http://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css" rel="stylesheet" type="text/css"></link>
      <div className="top">
        <BudgetOutput />
        
      </div>

      <div className="bottom">
        <BudgetInput 
          descValue={budgetObj.description || ''} //event.target.value
          onDescChange={event => setBudget({ type: "ON_DESC_CHANGE", payload: event.target.value })}
          onSelectChange={event => setBudget({ type: "ON_TYPE_CHANGE", payload: event.target.value })}
          type={budgetObj.type || ''}
          onBudgetSubmit={ () => setBudget({ type : 'SUBMIT_BUDGET' }) }
          budgetValue={budgetObj.value || ''}
          onValChange={event => setBudget({ type: "ON_VALUE_CHANGE", payload: event.target.value })}
        />

    
        <div className="container clearfix">
          <IncomeOutputList 
            list={incomes}
            removeIncome={ () => setBudget({ type: "REMOVE_ITEM" })} //not sure if this is correct?
          /> 
          <ExpenseOutputList
            list={expenses}
           // removeExpense={(index)=>removeExp(index)}
          />
          
        </div>
        
      </div>

    </div>
  )
};


export default App;

IncomeOutput.js

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

// list will be list of income objects
const IncomeOutputList = ({ list, removeIncome }) => {

    return (
        <div className="income__list">
            <div className="income__list--title">INCOME</div>
            {list.map((item, index, arr) => <IncomeOutput 
                                id={item.id} 
                                value={item.value} 
                                type={item.type} 
                                desc={item.description} 
                                
                                // error has something to do with the following line?
                                handleButton={()=>removeIncome(index)}
                                />
            )}
        </div>
    )
}

export default IncomeOutputList;

IncomeOutput.js

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

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

export default IncomeOutput;

ValueOutput.js

import React from 'react';
import Button from '../buttons/Button';

const ValueOutput = ({type, value, handleClick}) => {
    
    return (
        <>
            <div className="right clearfix">
                <div className="item__value">{type} {value}</div>
                <Button buttonType="item__delete--btn" handler={handleClick}/>
            </div>
        </>
    )
}

export default ValueOutput;

Button.js

import React from 'react';

const Button = ({buttonType, handler}) => (
    <>
        <div className="item__delete">
            <button className={buttonType} onClick={handler}>
                <i className="ion-ios-close-outline"></i>
            </button>
        </div>
    </>
)

export default Button;

I didn't run your code but just a guess

incomes: (index) => JSON.parse(localStorage.getItem("income")).splice(index,1),

incomes is a function not an array, so "incomes.map()" does not exist.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM