0

I'm looking to append the data in the UPLOAD_IMAGE to GET_IMAGES. Without having to re-rerendering the component. Or in other words, without having to refresh the page.

I get type errors whenever img is followed

  <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> 
               ........                 

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 array. The type error only happens if the images array is empty.

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

Should i use another lifecycle method ? because componentWillMount Cannot be called twice, just once. So given that array is empty, should i use something like shouldComponentUpdate to fetch the initial data ?

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 if array 
                                /// isn't empty

                ]   

            }

        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>        
      )
   }
}
3
  • Something doesn't look right. In your action-creator you pass newImage as an object, hence {...response.data} but then you use it as an array in your reducer; const newImage = action.newImage. then newImage[0]. You probably need to use [...response.data] instead Commented Jun 24, 2019 at 21:22
  • Hi Chris :) i tried that, and i got the same errors :( Commented Jun 24, 2019 at 21:25
  • wait i think something worked Commented Jun 24, 2019 at 21:58

2 Answers 2

1

You need to spread the existing images array inside your new state.

    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,
                },
                ...state.images

            ]   

        }

So with that you have a new state, with your new image first, followed by the initial images.

Sign up to request clarification or add additional context in comments.

4 Comments

you mean ...state ? using this.state doesn't work in redux.
let me give this a go.
Thanks man, heres the code, and heroku demo if you're interested in it. github.com/EliHood/ReactExpressPhotoShareApp
their is some things that im fixing, like only users can delete their posts, and passport-google issues that im having. but yeah thanks for your help Chris.
0

Fix. remove this line.

myImages[0] // pass

6 Comments

this worked, however Chris another issue arises, i make a post, but it doesn't pass the prevState, or something. For example, say you have an array of 3 objects, and you make a post, all of the objects are removed from the UI, and i just see the data i just posted. But on refresh i see all the items with the array.
Gotcha, let me post an answer below to assist you. See my answer above/below lol
Hi @BARNOWL, did you figure out your question about redirecting?
im going to work on it tomorrow, going to bed, if i can' figure it out ill post a better worded question about it.
I was literally like 30 seconds away from providing you a solution haha. But for sure :). GN friend.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.