G
G
gooseNjuice()2022-03-27 11:17:11
React
gooseNjuice(), 2022-03-27 11:17:11

Why inputRef.current=null?

There is an UploadUPApp component that receives the url of the android application from location.state.apk.
It renders the FileUploader, passes the inputRefs created with useRef() into it, and assigns it to the input.
The component passes location.state.apk and inputRef to the passURLtoInputFile function, where it adds the file by reference to input.current.files.
But instead of the expected behavior, passURLtoInputFile prints an input.current is null error to the console .
What am I doing wrong?

UploadUPApp

const initialApp = {
    applicationType: 'add a text',
    description: 'add a text',
    versionName: '',
    versionCode: '',
    privacyRating: 3,
    rating: 2,
    permissions: [],
    appSize: '',
    fileAppUrl: 'add a text',
    iconUrl: 'add a text',
    applicationName: '',
    applicationId: '',
    localFileUrl: '',
    localIconUrl: ''
};

const UploadUPApp = () => {
    const inputRef = useRef(null);
    const [iconBinaryFile, setIconBinaryFile] = useState('');
    const [appBinaryFile, setAppBinaryFile] = useState('');
    const [loading, setLoading] = useState(false);
    const [current, setCurrent] = useState(initialApp);
    const {
        applicationType,
        description,
        versionName,
        versionCode,
        privacyRating,
        rating,
        permissions,
        appSize,
        applicationName,
        applicationId,
        localFileUrl,
        localIconUrl,
    } = current;

    const updSpinner = isLoading => {
        setLoading(isLoading);
    };

    const location = useLocation();
    const onChange = (e) =>
        setCurrent({...current, [e.target.name]: e.target.value});

    const onSubmit = async (e) => {
        e.preventDefault();
    };

    const getAnalyzedAppData = (data, appSize, fileName, binaryFile, isIcon) => {
        console.log('getAnalyzedAppData');
        if (isIcon) {
            setIconBinaryFile(binaryFile);
            setCurrent({...current, localIconUrl: fileName});
        } else {
            setAppBinaryFile(binaryFile);
            setCurrent({
                ...current, applicationId: data.packageName,
                applicationName: data.label,
                versionName: data.versionName,
                versionCode: data.versionCode,
                permissions: data.usesPermissions,
                appSize: setCorrectSize(appSize),
                localFileUrl: fileName
            });
        }
    };

    useEffect(() => {
        if (location.state && location.state.apk) {
            updSpinner(true);
            passURLtoInputFile(inputRef, 'apk', location.state.apk)
                .then(async () => {
                    const fileUploaded = inputRef.current.files[0];
                    const fileName = fileUploaded.name;

                    const appSize = setCorrectSize(fileUploaded.size);
                    const formData = new FormData();
                    formData.append('file', fileUploaded);

                    try {
                        const res = await axios({
                            method: 'post',
                            url: 'api/admin/parse',
                            data: formData,
                            headers: {'Content-Type': 'multipart/form-data'},
                        });
                        getAnalyzedAppData(res.data, appSize, fileName, fileUploaded, false);
                        updSpinner(false);
                    } catch (error) {
                        console.log(error);
                        updSpinner(false);
                        window.alert('There was a problem to analyze application file');
                    }

                });

        }
    }, [inputRef]);

    if (loading) {
        return <Spinner/>;
    }
    return (
        <Fragment>
            <form onSubmit={onSubmit}>
                            <FileUploader buttonText='Analyze Application File'
                                          targetUrl='api/admin/parse'
                                          getAnalyzedAppData={getAnalyzedAppData} isIcon={false}
                                          updSpinner={updSpinner}
                                          needToParse
                                          externalApk={(location.state) && location.state.apk}
                                          ref={inputRef}
                            />
                            <FileUploader buttonText='Select Icon File'
                                          getAnalyzedAppData={getAnalyzedAppData} isIcon
                                          needToParse={false}
                            />
            </form>
        </Fragment>
    );
};


file uploader

const FileUploader = (props) => {
    // Create a reference to the hidden file input element
    const {hiddenFileInput, isIcon, getAnalyzedAppData, needToParse, updSpinner, targetUrl, buttonText, <b>ref</b>} = props;

    // Programmatically click the hidden file input element
    // when the Button component is clicked
    const handleClick = event => {
       event.preventDefault();
       hiddenFileInput.current.click();
    };

    // Call a function (passed as a prop from the parent component)
    // to handle the user-selected file
    const handleChange = async (event) => { // To-Do change to decoupled icon and APK file loader
        console.log('handling', event)
        const fileUploaded = event.target.files[0];
        const fileName = fileUploaded.name;
        // props.handleFile(fileUploaded);
        if (isIcon)
            getAnalyzedAppData(null, null, fileName, fileUploaded,true);
        else if (!needToParse)
            getAnalyzedAppData(null, null, fileName, fileUploaded,false);
        else {
            updSpinner(true);
            console.log(targetUrl);
            const appSize = fileUploaded.size;
            const formData = new FormData();
            formData.append('file', fileUploaded);

            try {
                const res = await axios({
                    method: "post",
                    url: targetUrl,
                    data: formData,
                    headers: { "Content-Type": "multipart/form-data" },
                })
                getAnalyzedAppData(res.data, appSize, fileName, fileUploaded,false);
                updSpinner(false);
            } catch (error) {
                console.log(error);
                updSpinner(false);
                window.alert('There was a problem to analyze application file');
            }
        }
    };
    return (
        <>
            <button type="button" onClick={handleClick}>
                {buttonText}
            </button>
            <input
                type="file"
                ref={ref}
                onChange={handleChange}
                style={{ display: 'none' }}
            />
        </>
    );
});


passURLtoInputFile

const passURLtoInputFile = async (input, name, url) => {
    try {
        const blob = await (await fetch(apkURL(url))).blob();
        const dt = new DataTransfer();
        dt.items.add(new File([blob], name, {type: blob.type}));
        input.current.files = dt.files;
        console.log('passed successfully:');
        console.dir(input.current.files);
    } catch (err) {
        console.error('the file wasn\'t passed:');
        console.error(err);
    }
};


when I navigate to the route corresponding to UploadUPApp and when passSomethingToInputFile is executed, input is null. Why is this happening?

Answer the question

In order to leave comments, you need to log in

1 answer(s)
W
wonderingpeanut, 2022-03-27
@gooseNjuice

The ref must be passed using forwardRef. // update. I'm wrong
useEffect doesn't track the change of ref objects, so it will be called once on the initial render.
If you want to send the loaded file through the parent component, the best practice is to create a state in the parent component and pass the state change handler to the child.

const [state, setState] = useState(undefined);
const handleChangeState = (newState) => {
  setState(newState);
}

return (
  <Component handler={handleChangeState} /> 
);
...
function Component({handler}) {
  return (
    <input onChange={(e) => handler(e.target.files)} />
);

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question