0

I am trying to create a Game, await for that action to be fulfilled, get the id of the recently created game and dispatch it with the action to upload a photo.

import React, { useState, useEffect } from 'react'
import FormGroup from '../UI/FormGroup'
import '../../styles/components/Auth/RegisterForm.css'
import { useDispatch, useSelector } from 'react-redux';
import { createGame, uploadPhoto } from '../../slices/gameSlice';
import { fetchEmployees } from '../../slices/employeeSlice';


function GameForm() {

  const { employees: employeesList } = useSelector((state) => state.employees);
  const dispatch = useDispatch();
  const [games, setGames] = useState([]);
  useEffect(() => {
    dispatch(fetchEmployees());
  }, [dispatch]);

    const [formData, setFormData] = useState({
        name: "",
        description: "",
        employees: [],
        hours: [
          {
            opening: "",
            closing: ""
          }
        ],
        photo:null
      });
    
      const { name, description,  hours, employees, photo } = formData;
    
      const onChange = (e) => {
        if (e.target.name === 'opening' || e.target.name === 'closing') {
          setFormData({
            ...formData,
            hours: [
              {
                ...hours[0],
                [e.target.name]: e.target.value,
              },
            ],
          });
        } else if (e.target.name === 'employees') {
          const selectedOptions = Array.from(e.target.options)
            .filter((option) => option.selected)
            .map((option) => option.value);
          setFormData({ ...formData, [e.target.name]: selectedOptions });
        } else if (e.target.name === 'photo') {
          setFormData({ ...formData, photo: e.target.files[0] }); // Update this line
        } else {
          setFormData({ ...formData, [e.target.name]: e.target.value });
        }
      };
      

      const onSubmit = async (e) => {
        e.preventDefault();
        console.log('Form submitted', formData)
        try {
          const gameData = {
            name,
            description,
            hours,
            employees
          };
      
          const createdGame = await dispatch(createGame(gameData)).unwrap();
          setGames([...games, createdGame]);
          await dispatch(uploadPhoto({ id: createdGame._id, file: photo })).unwrap();
        } catch (error) {
          console.log(error);
        }
      };
      
      

    const formFields = [
        {
          name: "name",
          type: "text",
          placeholder: "Name",
          label: "Name",
          value: name,
          onChange: onChange
        },
        {
          name: "description",
          type: "text",
          placeholder: "Description",
          label: "Description",
          value: description,
          onChange: onChange
        },
        {
            name: "photo",
            type: "file",
            placeholder: "Game Photo",
            label: "Game Photo",
            onChange: onChange
        },
        {
         name: "opening",
         type: "datetime-local",
         placeholder: "Opening Hour",
         label: "Opening Hour",
         value: hours[0].opening,
         onChange: onChange
        },
        {
          name: "closing",
          type: "datetime-local",
          placeholder: "Closing Hour",
          label: "Closing Hour",
          value: hours[0].closing,
          onChange: onChange
         },
         {
          name: "employees",
          type: "select",
          placeholder: "Employees",
          label: "Employees",
          value: employees,
          options: employeesList.filter(employee => employee.type === 'manager').map((employee) => ({
            value: employee._id,
            label: `${employee.name} ${employee.lastName}`,
          })),
          multiple: true,
          onChange: onChange
        },
      ]
      
      /* console.log(formData) */
      
  return (
    <div style={{display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100vh', width: '100%'}}>
      <form className='form_container' onSubmit={onSubmit} encType='multipart/form-data'>
        {formFields.map((field) => (
          <FormGroup key={field.name} {...field} multiple={field.multiple} />
        ))}
        <button type="submit">Register</button>
      </form>
    </div>
  )
}

export default GameForm 

My form has an encType=multipart/form-data

And the following is my gameSlice

import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import axios from 'axios';

const initialState = {
  games: [],
  isLoading: false,
};

export const createGame = createAsyncThunk(
  'games/createGame',
  async (game, { rejectWithValue }) => {
    try {
      const config = {
        headers: {
          'Content-Type': 'application/json',
        },
      };
      const body = JSON.stringify(game);
      const response = await axios.post(
        'http://localhost:5000/api/games/register',
        body,
        config
      );
      return response.data.data;
    } catch (error) {
      return rejectWithValue(error.response.data);
    }
  }
);

export const uploadPhoto = createAsyncThunk(
  'games/uploadPhoto',
  async ({ id, file }, { rejectWithValue }) => {
    try {
      const formData = new FormData();
      formData.append('file', file);

      const response = await axios.put(
        `http://localhost:5000/api/games/${id}/photo`,
        formData,
        {
          headers: {
            'Content-Type': 'multipart/form-data',
          },
        }
      );

      if (!response || !response.data) {
        throw new Error('Invalid response received');
      }

      return response.data;
    } catch (error) {
      return rejectWithValue(error.response?.data || error.message);
    }
  }
);

export const getGames = createAsyncThunk(
  'games/getGames',
  async (_, { rejectWithValue }) => {
    try {
      const response = await axios.get('http://localhost:5000/api/games');
      console.log(response.data);
      return response.data.data;
    } catch (error) {
      return rejectWithValue(error.response.data);
    }
  }
);

export const gameSlice = createSlice({
  name: 'games',
  initialState,
  reducers: {},
  extraReducers: (builder) => {
    builder
      .addCase(getGames.pending, (state) => {
        state.isLoading = true;
      })
      .addCase(getGames.fulfilled, (state, action) => {
        state.isLoading = false;
        state.games = action.payload;
      })
      .addCase(getGames.rejected, (state) => {
        state.isLoading = false;
        state.games = [];
      })
      .addCase(createGame.pending, (state) => {
        state.isLoading = true;
      })
      .addCase(createGame.fulfilled, (state, action) => {
        state.isLoading = false;
        state.games.push(action.payload);
      })
      .addCase(createGame.rejected, (state) => {
        state.isLoading = false;
        state.games = [];
      })
      .addCase(uploadPhoto.pending, (state) => {
        state.isLoading = true;
      })
      .addCase(uploadPhoto.fulfilled, (state, action) => {
        state.isLoading = false;
        const game = state.games.find(
          (game) => game._id === action.payload._id
        );
        if (game) {
          game.photo = action.payload.photo;
        }
      })
      .addCase(uploadPhoto.rejected, (state) => {
        state.isLoading = false;
        state.games = [];
      });
  },
});

export const {} = gameSlice.actions;

export default gameSlice.reducer;

When i submit my form the createGame action is fullfilled and the game is created, but then the uploadPhoto is rejected. With an error message of Unexpected end of form.

This is my method in my controller:

// @desc   Upload photo to a game
// @route  PUT /api/games/:id/photo
// @access Private

exports.uploadPhoto = asyncHandler(async (req, res, next) => {
  const game = await Game.findById(req.params.id);

  if (!game) {
    return next(
      new ErrorResponse(`Game not found with id of ${req.params.id}`, 404)
    );
  }

  if (!req.files) {
    return next(new ErrorResponse(`Please upload a file`, 400));
  }

  const file = req.files.file;

  // Make sure the image is a photo
  if (!file.mimetype.startsWith('image')) {
    return next(new ErrorResponse(`Please upload an image file`, 400));
  }

  // Check filesize
  if (file.size > process.env.MAX_FILE_UPLOAD) {
    return next(
      new ErrorResponse(
        `Please upload an image less than ${process.env.MAX_FILE_UPLOAD}`,
        400
      )
    );
  }

  // Create custom filename
  file.name = `photo_${game._id}${path.parse(file.name).ext}`;

  file.mv(`${process.env.FILE_UPLOAD_PATH}/${file.name}`, async (err) => {
    if (err) {
      console.error(err);
      return next(new ErrorResponse(`Problem with file upload`, 500));
    }

    await Game.findByIdAndUpdate(req.params.id, { photo: file.name });

    res.status(200).json({
      success: true,
      data: file.name,
    });
  });
});
tukitooo1998
  • 337
  • 10

0 Answers0