Juan Pablo SR
Juan Pablo SR

Reputation: 11

Why do I keep getting this hook invalid call error?

I'm coding a custom hook but I always get this error:

Uncaught (in promise) Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:

  1. You might have mismatching versions of React and the renderer (such as React DOM)
  2. You might be breaking the Rules of Hooks
  3. You might have more than one copy of React in the same app

I stop passing setState but it still doesn't work.

Custom hook, useStorage.js

import React, { useState, useEffect } from "react";
import { uploadFile, getFileUrl } from "../services/uploadFile";

export function useStorage(file, fileName , folderPath){
    const [progress, setProgress] = useState(0);
    const [error, setError] = useState(null);
    const [url, setUrl] = useState(null);

    useEffect(() => {
        if (file) {
            uploadFile(file , fileName , folderPath)
            .on("state_changed",
            (snapshot) => {
                const progress = Math.round((snapshot.bytesTransferred / snapshot.totalBytes) * 100);
                setProgress(progress);
            },
            (error) => {
                setError(error);
            },
            () => {
                getFileUrl(uploadFile.snapshot.ref).then((downloadURL) => {
                    setUrl(downloadURL)
                });
            }
            );
        }
    }, [file]);

    return { progress, url, error };
};

uploadFile.js

import { storage } from "../../../firebase/configuration"
import {ref, uploadBytesResumable, getDownloadURL} from "firebase/storage"


export const uploadFile = (folderPath , fileName, file) =>{
    const storageRef = ref(storage , `${folderPath}${fileName}`)
    const uploadTask = uploadBytesResumable(storageRef, file);

    return uploadTask;
}


export const getFileUrl = async(ref) =>{
    return await getDownloadURL(ref)
}

I use the hook in this component(dataFormljsx)

import { Grid, TextField, Button, CssBaseline } from '@mui/material';
import Typography from '@mui/material/Typography';
import { MobileDatePicker } from '@mui/x-date-pickers/MobileDatePicker';
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
import esEsLocale from 'date-fns/locale/es';
import { useState } from 'react';
import { uploadFile } from '../../services/uploadFile';
import { updateDocument } from '../../services/updateDocument';
import { useStorage } from '../../hooks/useStorage';
import { useUpdateUser } from '../../hooks/useUpdateUser';

const userExample = {
    name : "John",
    lastName : "Test", 
    secondLastName : "test",
    codeforcesUsername : "test", 
    email : "[email protected]", 
    nickname : "",
    birthdate : new Date(),
    career : "",
    cellphone : "",
    currentSemester:  "",
    github : "",
    facebook : "",
    instagram : "",
    faculty : "",
    omegaup : "",
    generation : 2,
    inscription : false,
    photo : "", //missing
    problemsSolved : null,
    role : "student",
    rules : false, //missing
    team : "",
    inscriptionComprobant : ""
}

const ProfileDataForm = () =>{
    
    const [formValues , setFormValues] = useState(userExample);
    //let navigate = useNavigate();


    const handleInputChange = (e) => {
        const { name, value } = e.target;
        setFormValues({
          ...formValues,
          [name]: value,
        });
    };
    
    const handleSubmit = async (event) => {
        event.preventDefault();

        var uid = "e9O4OfTsNRfPITvtcsF97YgMaYT2"

        //upload comprobant 
        // uplaod photo
        const {photo , inscriptionComprobant} = formValues
        const {progressPhoto , urlPhoto , errorPhoto}  = useStorage(photo , "ejem.jpeg" , "images/userPhotos")
        const {progressPdf , urlPdf , errorPdf}  = useStorage(inscriptionComprobant, "ejem.pdf" , "pdfs/inscriptionComprobant")

        if(!errorPhoto && errorPdf){
            const [newDoc , updateError] = useUpdateUser(uid, 
                {
                    ...formValues,
                    photo : urlPhoto,
                    inscriptionComprobant : urlPdf
                }
            ) 
            if(updateError) console.log(updateError);
            else setFormValues(newDoc);
        }
    }

    const handleDatePicker = (value) => {
        setFormValues({
            ...formValues,
            birthdate : value
        })
    }

    const handleOnChangeFile = (e) => {
        const { name, files } = e.target;
        setFormValues({
          ...formValues,
          [name]: files[0],
        });
    }
    console.log(formValues)
    return(
        <form>
        <CssBaseline/>
        <Grid container spacing={2} >
            <Grid item xs={4}>
                <TextField 
                id = "name" 
                label = "Nombres" 
                variant="outlined" 
                fullWidth margin="normal" 
                onChange={handleInputChange} 
                type = "text" 
                value = {formValues.name} 
                name = "name" 
                required />
            </Grid>
            <Grid item xs={4}>
                <TextField id = "lastName" 
                label = "Primer Apellido"  
                variant="outlined" 
                fullWidth 
                margin="normal" 
                onChange={handleInputChange} 
                type = "text" 
                value = {formValues.lastName} 
                name = "lastName" 
                required/>
            </Grid>
            <Grid item xs={4}>
                <TextField id = "secondLastName" 
                label = "Segundo Apellido"  
                variant="outlined" 
                fullWidth 
                margin="normal" 
                onChange={handleInputChange} 
                type = "text" 
                value = {formValues.secondLastName} 
                name = "secondLastName" />
            </Grid>
            <Grid item xs={3}>
                <TextField id = "nickName" 
                label = "Apodo"  
                variant="outlined" 
                fullWidth 
                margin="normal" 
                onChange={handleInputChange} 
                value = {formValues.nickname} 
                name = "nickname" 
                required />
            </Grid>
            <Grid item xs={5}>
                <TextField id = "email" 
                label = "Correo"  
                variant="outlined" 
                fullWidth 
                margin="normal" 
                type= "email" 
                onChange={handleInputChange} 
                value = {formValues.email} 
                name = "email" 
                required/>
            </Grid>
            <Grid item xs={4}>
                <TextField id = "cellphone" 
                label = "Número Celular"  
                variant="outlined" 
                fullWidth 
                margin="normal" 
                onChange={handleInputChange} 
                value = {formValues.cellphone} 
                name = "cellphone" 
                required />
            </Grid>
            <LocalizationProvider dateAdapter={AdapterDateFns} adapterLocale = {esEsLocale}>
                <MobileDatePicker
                label="Fecha de nacimiento"
                inputFormat="dd/MM/yyyy"
                value={formValues.birthdate}
                onChange={handleDatePicker}
                renderInput={(params) => 
                <Grid item xs= {3}>
                    <TextField {...params}  margin = "normal"/>
                </Grid>
                }
                />
            </LocalizationProvider>
            <Grid item xs={3}>
                <TextField id = "faculty" 
                label = "Facultad"  
                variant="outlined" 
                fullWidth 
                margin="normal" 
                onChange={handleInputChange} 
                value = {formValues.faculty} 
                name = "faculty" 
                required />
            </Grid>
            <Grid item xs={6}>
                <TextField id = "career" 
                label = "Carrera actual"  
                variant="outlined" 
                fullWidth 
                margin="normal" 
                onChange={handleInputChange} 
                value = {formValues.career} 
                name = "career" 
                required />
            </Grid>
            <Grid item xs={3}>
                <TextField id = "currSemester" 
                label = "Semestre Actual"  
                variant="outlined" 
                fullWidth 
                margin="normal" 
                onChange={handleInputChange} 
                value = {formValues.currentSemester} 
                name = "currentSemester" 
                type= "number"
                required />
            </Grid>
            <Grid item xs={4}>
                <TextField id = "codeforcesUsername" 
                label = "Usuario de codeforces"  
                variant="outlined" 
                fullWidth 
                margin="normal" 
                onChange={handleInputChange} 
                value = {formValues.codeforcesUsername} 
                name = "codeforcesUsername" 
                required />
            </Grid>
            <Grid item xs={5}>
                <TextField id = "github" 
                label = "Github"  
                variant="outlined" 
                fullWidth 
                margin="normal" 
                onChange={handleInputChange} 
                value = {formValues.github} 
                name = "github" 
                required />
            </Grid>
            <Grid item xs={4}>
                <TextField id = "facebook" 
                label = "Facebook"  
                variant="outlined" 
                fullWidth 
                margin="normal" 
                onChange={handleInputChange} 
                value = {formValues.facebook} 
                name = "facebook" 
                required />
            </Grid>
            <Grid item xs={4}>
                <TextField id = "instagram" 
                label = "Instagram"  
                variant="outlined" 
                fullWidth 
                margin="normal" 
                onChange={handleInputChange} 
                value = {formValues.instagram} 
                name = "instagram" 
                required />
            </Grid>
            <Grid item xs={4}>
                <TextField id = "omegaup" 
                label = "Omegaup"  
                variant="outlined" 
                fullWidth 
                margin="normal" 
                onChange={handleInputChange} 
                value = {formValues.omegaup} 
                name = "omegaup" 
                required />
            </Grid>
            <Grid item xs={12}>
                <Button variant="contained" component="label" >
                    Subir foto de perfil
                    <input hidden accept="image/*"  type="file" size = "2mb" name = "photo" onChange = {handleOnChangeFile}
                    />
                </Button>
            </Grid>
            <Grid item xs={12}>
                <Button variant="contained" component="label" >
                    Subir comprobante de inscripcion
                    <input hidden accept="application/pdf"  type="file" size = "5mb" onChange={handleOnChangeFile} 
                    name = "inscriptionComprobant" 
                    />
                </Button>
            </Grid>
            <Grid item xs={12}>
                <Button variant="contained" onClick = {handleSubmit}>
                    Actualizar
                </Button>
            </Grid>
        </Grid>
    </form>
    )
}

export default ProfileDataForm

Upvotes: 0

Views: 482

Answers (1)

Lucas Emanuel
Lucas Emanuel

Reputation: 626

You cannot call the hook inside a function of the functional component, the hook call needs to be in the scope of the functional component, here I explain better

const ProfileDataForm = () =>{
    
    const [formValues , setFormValues] = useState(userExample);
    //let navigate = useNavigate();

    // Call useStorage hook and all hooks here


    const handleInputChange = (e) => {
        const { name, value } = e.target;
        setFormValues({
          ...formValues,
          [name]: value,
        });
    };
    
    const handleSubmit = async (event) => {
        event.preventDefault();

        var uid = "e9O4OfTsNRfPITvtcsF97YgMaYT2"

        //upload comprobant 
        // uplaod photo
        const {photo , inscriptionComprobant} = formValues
        const {progressPhoto , urlPhoto , errorPhoto}  = useStorage(photo , "ejem.jpeg" , "images/userPhotos")
        const {progressPdf , urlPdf , errorPdf}  = useStorage(inscriptionComprobant, "ejem.pdf" , "pdfs/inscriptionComprobant")

        if(!errorPhoto && errorPdf){
            const [newDoc , updateError] = useUpdateUser(uid, 
                {
                    ...formValues,
                    photo : urlPhoto,
                    inscriptionComprobant : urlPdf
                }
            ) 
            if(updateError) console.log(updateError);
            else setFormValues(newDoc);
        }
    }

    const handleDatePicker = (value) => {
        setFormValues({
            ...formValues,
            birthdate : value
        })
    }

Upvotes: 1

Related Questions