0
votes

I'm trying to append the data in the reducer to a mapped array. This reducer works only if their is data within the images array. But gives a type error on multiple instances when img is being called .

for example

TypeError: Cannot read property 'image_title' of undefined

on refresh i see the new data, and i can add data, and i can see the updated state. The type error problem only happens if the images array is empty.

  const myImages = newState.images 
  console.log(myImages); // renders empty array []

I would like to be able to append the data to the empty array, and show the data without re render/refresh or any type errors errors.

data structure given that their is existing data in the array.

0:{
  "id": 71,
  "image_title": "ii",
  "img_url": "https://*********",
  "created_at": "2019-06-24T02:36:48.359Z",
  "updated_at": "2019-06-24T02:36:48.359Z",
  "user_id": 1,
  "user": {
    "id": 1,
    "googleId": null,
    "username": "a******",
    "password": "**********",
    "email": "a********",
    "created_at": "2019-06-23T18:57:17.253Z",
    "updated_at": "2019-06-23T18:57:17.253Z"
  },
  "comments": []
}

reducer

import { GET_IMAGES, POST_COMMENT, DELETE_IMAGE, UPLOAD_IMAGE } from '../actions/types';

const initialState = {
    images:[],

}

export default  (state = initialState, action) => {
    switch (action.type) {
        case GET_IMAGES:
            console.log(action.data);
            return{
                ...state,
                images:action.data
            }
        case UPLOAD_IMAGE:
            const newState = {...state}
            const myImages = newState.images 
            // console.log(myImages); // empty array
            const newImage = action.newImage
            console.log(newImage[0]); // gets the new uploaded image. 

            return {
                images:[

                    {
                        id: newImage[0].id,
                        user:{
                            username:newImage[0].user.username
                        },
                        comments:{
                          comment_body: newImage[0].comments.comment_body  
                        },
                        image_title: newImage[0].image_title,
                        img_url: newImage[0].img_url,
                    },

                    // myImages[0] // pass the previous images

                ]   

            }

        default:
            return state;
    }
}

action

// upload image 
export const uploadImage = data =>  {
   return (dispatch) => {
    Axios.post('/images/upload', data).then((response) => {
        const newImage = {...response.data}
        console.log(newImage);

        dispatch({type:UPLOAD_IMAGE, newImage})
        // history.push("/dashboard");
    });

   } 

}    
// get images
export const getImages = () => {
    return async (dispatch) => {
       const url =  await Axios.get('/images/uploads')
       const data = url.data;
        dispatch({ 
            type: GET_IMAGES,
            data
        })

    }
}

Dashboard.js

import React, { Component } from "react";
import Button from '@material-ui/core/Button';
import TextField from '@material-ui/core/TextField';
import Grid from '@material-ui/core/Grid';
import Typography from '@material-ui/core/Typography';
import ImageUploader from 'react-images-upload';
import ImageContainer from "./ImageContainer"
import {connect} from 'react-redux';
import {getImages, deleteImage, uploadImage} from '../actions/imageActions';
import dashboardStyles from '../styles/dashboardStyles';
import {withStyles} from '@material-ui/core/styles';
import {compose} from 'redux';
class Dashboard extends Component{
    constructor(props){
        super(props);
        this.state = {
            image_url: '', 
            description:'',
            upload:false,
            isComment:false,
            comment_body:''
        }
    }
    handleUpload =  file =>  {
        const data = new FormData()
        const image = file[0]
        // console.log(this.state.description)
        // data.append('ourImage', this.state.description)
        data.append('ourImage',image, this.state.description )
        this.props.uploadImage(data);

        this.setState({
            description: ''
        })



    }
    handleChange = (e) => {
        // e.preventDefault();
        this.setState({
            [e.target.name]: e.target.value
        })
        // console.log(this.state.description)
    }
    componentDidMount(){
        this.props.getImages();
        console.log(this.props.image.images);
    }


      .........
      {image.images.length > 0 ? (
       image.images.map( (img, i) => (   
           <div key={i}>
               <ImageContainer img={img} deleteImg={() => this.deleteImg(img.id)}/>
           </div>      
       ))
      ) : (
          <div>
              <Grid item md={8}>
                  <Typography>No Images yet</Typography>
               </Grid>
          </div>
      )}


const mapStateToProps = (state) => ({
   image: state.image
})
const mapDispatchToProps = (dispatch) => ({
   getImages: () => dispatch(getImages()),
   uploadImage: (data) => dispatch(uploadImage(data))
})
export default compose(connect(mapStateToProps, mapDispatchToProps), withStyles(dashboardStyles))(Dashboard)

image container

  render(){
       const { img, deleteImg, classes } = this.props
       return(
           <Grid item sm={12} md={12} className={classes.imageGridItem}>
               <Paper className={classes.imageContainerPaper}>
         {/* // empty image_title */}
               <Typography className={classes.imageTypographyTitle} variant="h4" align="center">{img.image_title}</Typography> 
               <Divider className={classes.imageDivider} variant="middle" />
               <Image image_url={img.img_url} />   
               <Typography variant="h6" align="center">{img.user.username}</Typography> 
               <Typography variant="h6" align="center">{moment(img.created_at).calendar()}</Typography> 
               ........                       
        </Grid>        
      )
   }
}
1
Just an important note about terminology - 'mutate' means to change the value(s) of an object while maintaining the same reference to it. In functional programming (like within the use of tools like redux) mutation is to be avoided.dwjohnston

1 Answers

-1
votes

You are sending object but accessing as array.

const newImage = {...response.data}


const newImage = action.newImage

How to fix

const newImage = [...response.data]; // send as array