Vishwanath B
Vishwanath B

Reputation: 89

Uploading images along with other text inputs in React?

It's extremely weird that all the tutorials I've found online show how to upload an image, but do not show how to do it with other text inputs included.

Thus, I've hit a roadblock trying to upload images, as well as other textual data in the same form. Spent hours searching on SO and Google, but couldn't find anything that fit my situation.

I'm using React & Redux, and the express-fileupload package for file uploads.

Anyway, here's what I've tried:

Backend

campgroundRoutes.js

const express = require('express');
const router = express.Router();
const fileUpload = require('express-fileupload');
router.use(fileUpload());

const Campground = require(`../../models/campground`);
const checkAuth = require('../../middleware/check-auth');


router.post('/', checkAuth, (req, res, next) => {

  const file = req.files.file;
  console.log('req.files: ', req.files); // req.files is undefined

  uploadPath = './assets/uploadedImages/' + file.name;

  file.mv(uploadPath, err => {
    if (err) {
      console.error('Error: ', err);
      return res
        .status(500)
        .json({ error: err, message: 'Failed to upload file' });
    }
    res.json({ fileName: file.name });
  });

  const campgroundToPost = new Campground({
    title: req.body.title,
    description: req.body.description,
    cost: req.body.cost,
    imageName: file.name,
    _author: {
      id: req.userData.userId,
      firstName: req.userData.firstName
    }
  });
  campgroundToPost
    .save()
    .then(result => res.status(200).json({ campground: result }))
    .catch(err => res.status(400).send(`Failed to add campground, ${err}`));
});

Frontend

addCampground.js

import React, { Component } from 'react';
import TextField from '@material-ui/core/TextField';
import Card from '@material-ui/core/Card';
import CardContent from '@material-ui/core/CardContent';
import Typography from '@material-ui/core/Typography';

import Button from '@material-ui/core/Button';
import '../../styles/addCampground.css';

import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';

import {
  actionAddCampground,
  getCampgroundDetails
} from './actions/campgroundActions';

class AddCampground extends Component {
  constructor(props) {
    super(props);
    this.state = {
      title: '',
      description: '',
      cost: '',
      selectedImage: null
    };
  }

  handleChange = e => {
    const { name, value } = e.target;
    this.setState({
      [name]: value
    });
  };

  uploadImage = e => {
    this.setState(
      {
        selectedImage: e.target.files[0]
      },
      () => console.log(this.state.selectedImage)
    );
  };

  addCampground = () => {
    const title = this.state.title;
    const description = this.state.description;
    const cost = this.state.cost;
    const data = new FormData();
    data.append('file', this.state.selectedImage);
    this.props
      .actionAddCampground({
        title,
        description,
        cost,
        data
      })
      .then(res => console.log(res))
      .catch(err => console.log('Error: ', err));
    this.props.history.push('/home');
  };

  render() {
    return (
        <Card className="add-campground-card">
          <CardContent>
            <Typography
              style={{ fontWeight: '400' }}
              className="text-center"
              variant="h6"
              component="h6">
              Add Your Campground
            </Typography>
          </CardContent>
          <TextField
            autoComplete="off"
            name="title"
            className="textfield"
            label="Campground name"
            variant="outlined"
            value={this.state.title}
            onChange={e => this.handleChange(e)}
          />
          <TextField
            autoComplete="off"
            name="description"
            className="textfield"
            label="Campground description"
            variant="outlined"
            value={this.state.description}
            onChange={e => this.handleChange(e)}
          />
          <TextField
            autoComplete="off"
            name="cost"
            className="textfield"
            type="number"
            label="Campground cost"
            variant="outlined"
            value={this.state.cost}
            onChange={e => this.handleChange(e)}
          />  
          <input onChange={this.uploadImage} type="file" name="file" />
          <Button
            className="add-campground"
            variant="contained"
            color="primary"
            onClick={this.addCampground}>
            Add Campground
          </Button>
        </Card>
    );
  }
}

const mapStateToProps = state => {
  return {
    campground: state.campgroundList.singleCampground
  };
};

const mapDispatchToProps = dispatch => {
  return bindActionCreators(
    {
      actionAddCampground,
      getCampgroundDetails
    },
    dispatch
  );
};

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(AddCampground);

campgroundActions.js

// Only including add campground action here as only that's relevant

import {
  ADD_CAMPGROUND,
} from '../actionTypes/types';

import axios from 'axios';
import { authHeader } from '../../../helpers/auth-header';

const API_URL = `http://localhost:5000/api`;

export const actionAddCampground = campground => {
  return async dispatch => {
    try {
      const res = await axios.post(`${API_URL}/campgrounds`, campground, {
        headers: authHeader()
      });
      dispatch({
        type: ADD_CAMPGROUND,
        payload: res
      });
      return res.data;
    } catch (err) {
      return console.log(err);
    }
  };
}; 

authHeader.js

export const authHeader = () => {
  let user = JSON.parse(localStorage.getItem('user'));
  let token = localStorage.getItem('token');
  if (user && token) {
    return {
      Authorization: token
    };
  } else {
    return {};
  }
};

This is error I get in the backend:

TypeError: Cannot read property 'file' of undefined

I cannot figure out where I'm going wrong.

Upvotes: 2

Views: 3051

Answers (1)

Mojo
Mojo

Reputation: 518

A few days back Even I was facing the same problem where I have to send the file and another Input field Eventually this below code helped me while making an API call see if it is any help to you

//-------------To insert Documents and InputField--------------//

export function documentsInsert( documentName, document ) {

var formData = new FormData();
formData.append("documentName", documentName)
formData.append("document", document)

return request2({
    url: xyzzz,
    method: 'POST',
    body: formData
});
}

Upvotes: 3

Related Questions