Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is only the last item in a map function being changed in react app?

Tags:

I have a component which renders a file list. The methods on it are simple add, update, and remove. I'm experiencing behavior that screams closure problem but I can't figure out what. The component stores the list of files in state as an array. So when rendering I just map over them. Updating items works as you'd expect it to so I believe the correct ids are being passed for that method, but removal always passes the id of the last item mapped over.

So if I have 3 items added: 1. file #1 2. file #2 3. file #3

and I click to update #2 - all works as expected file #2 is set to whatever file I placed in there and state reflects the correct list.

but if I try to remove file #1 or #2 file #3 gets removed.

What am I missing?

// npm modules
import React, { useState } from 'react';
import randomstring from 'randomstring';

// components
import { PrimaryButton, InlineButton } from "../buttons/buttons";
import { AdminInput } from "../inputs/textInputs";

export const AdminSlides = ({ setSlides }) => {
  const [ state, updateState ] = useState({
    slides: [],
  });

  function setState(value){
    updateState( prevState => ({
      ...prevState,
      ...value
    }));
  }

  function addSlide(){
    const slides = state.slides;
    const id = randomstring.generate({ length: 5, charset: 'alphabetic' })
    slides.push({ id, slide: null });
    setState({ slides });

    // send current state to parent component
    if (setSlides) setSlides(state.slides);
  }

  function removeSlide(id){
    const slides = state.slides.filter(item => item.id !== id);
    setState({ slides });
  }

  function setSlide(file, id){
    const slides = state.slides.map(item => {
      if (item.id === id) item.slide = file;
      return item;
    });
    setState({ slides });

    // send current state to parent component
    if (setSlides) setSlides(state.slides);
  }
  return (
      <div>
        {
          state.slides.map(slide => (
              <div className='m-b:1'>
                <AdminInput
                    type='file'
                    onChange={e => setSlide(e.target.files[0], slide.id)}
                    className='m-b:.5'
                />
                <InlineButton className='m:0' onClick={()=>removeSlide(slide.id)}>Remove</InlineButton>
              </div>
          ))
        }
        <PrimaryButton onClick={addSlide}>Add Slide</PrimaryButton>
      </div>
  )
};
like image 572
KAT Avatar asked Dec 26 '19 17:12

KAT


1 Answers

This kind of unexpected behaviour is common when you don't use keys or use keys as index.

Your problem will be solved just by using slide.id as key.

 {
          state.slides.map(slide => (
              <div className='m-b:1' key={slide.id}>
                <AdminInput
                    type='file'
                    onChange={e => setSlide(e.target.files[0], slide.id)}
                    className='m-b:.5'
                />
                <InlineButton className='m:0' onClick={()=>removeSlide(slide.id)}>Remove</InlineButton>
              </div>
          ))
        }
like image 129
kooskoos Avatar answered Oct 20 '22 06:10

kooskoos