I have react hook named Np02 which shows photos selected by file input and saves them into state. When user selects files, they are shown in grid to him. This works, but there is functionality where I want to remove selected image and this does not. The function removeImage runs, outputs array I expect, but the state wont change. I already tried to move setImages to the function removeImages or another places, but the state itself wont change and when I add useEffect on images, It changes only when I select images, but not when I want to remove them. Why is state not changed?
const MAX_IMAGES = 5;
const blankImage = 'https://static.thenounproject.com/png/187803-200.png';
function fillWithBlanks(arr) {
const blanks = new Array(MAX_IMAGES - arr.length).fill(blankImage);
return [...arr, ...blanks];
};
const Np02 = ({setForm}) => {
const classes = useStyles();
const fileInput = useRef<HTMLInputElement | null>(null);
const dispatch = useDispatch();
const NPData = useSelector(getNPStepData);
const [values, setValues] = useState(NPData[1]);
const [images, setImages] = useState<any[]>(fillWithBlanks([]));
useEffect(() => {
setTimeout(() => {
setForm(checkFormValidity);
}, 200);
}, [values]);
const checkFormValidity = (): boolean => {
dispatch(submitStep(1, values));
return true;
};
const imageInputChanged = (e) => {
const files = e.target.files;
const result: any = [];
let fileCounter = 0;
for (var i = 0; i < files.length; i++) {
const file = files.item(i);
let reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = (e) => {
result.push(reader.result);
fileCounter++;
if (fileCounter >= files.length) {
let uploadedImg = [...(images.filter((value) => value !== blankImage)), ...result];
if (MAX_IMAGES < uploadedImg.length) {
uploadedImg = uploadedImg.slice(uploadedImg.length - MAX_IMAGES);
}
setImages(fillWithBlanks(uploadedImg));
}
};
}
};
const removeImage = (id) => {
let arr = images;
arr[id] = blankImage;
console.log(arr); // this outputs right version of array
return arr;
};
return (
<div>
<label htmlFor="raised-button-file" className={classes.buttonLabel}>
<Button variant="contained" color="secondary" component="span">
Nahraj fotky
</Button>
</label>
<FormHelperText className={classes.helperText}>Nahraj alespoň dvě různé fotky tvého
předmětu</FormHelperText>
<ImagesGrid images={images} imageClicked={(id) => setImages(removeImage(id))}/>
<TextField
className={classes.textarea}
label="Popis"
variant="filled"
helperText="Zde popiš předmět, pro případné zájemce"
multiline
rows={6}
value={values.desc}
onChange={(e) => setValues({desc: e.target.value})}
/>
<input
ref={fileInput}
accept="image/*"
className={classes.input}
id="raised-button-file"
multiple
type="file"
onChange={imageInputChanged}
/>
</div>
);
};
export default Np02;
Here is component responsible for drawing images to grid.
const ImagesGrid = (props: iImagesGrid) => {
const classes = useStyles();
const spacing = 0;
return (
<div className={classes.root}>
<Grid className={classes.fullHeight} container spacing={spacing} direction="row"
justify="center"
alignItems="stretch">
<Grid item xs={6} onClick={() => props.imageClicked(0)}>
<GridImage url={props.images[0]} title={'teehee'} />
</Grid>
<Grid item xs={6} className={classes.fullHeight}>
<Grid container className={classes.halfHeight} item xs={12}>
<Grid item xs={6} onClick={() => props.imageClicked(1)}>
<GridImage url={props.images[1]} title={'teehee'}/>
</Grid>
<Grid item xs={6} onClick={() => props.imageClicked(2)}>
<GridImage url={props.images[2]} title={'teehee'}/>
</Grid>
</Grid>
<Grid item className={classes.halfHeight} container xs={12}>
<Grid item xs={6} onClick={() => props.imageClicked(3)}>
<GridImage url={props.images[3]} title={'teehee'}/>
</Grid>
<Grid item xs={6} onClick={() => props.imageClicked(4)}>
<GridImage url={props.images[4]} title={'teehee'}/>
</Grid>
</Grid>
</Grid>
</Grid>
</div>
);
};
export default ImagesGrid;
You're mutating the existing state here:
arr[id] = blankImage;
Clone the existing array first instead, so as not to mutate anything currently in state:
const newArr = [...images];
newArr[id] = blankImage;
return newArr;