gooseNjuice
@gooseNjuice
JavaScript Fullstack

Почему inputRef.current=null?

Существует компонент UploadUPApp, который получает из location.state.apk url андроид-приложения.
Он рендерит FileUploader, передаёт в него inputRef, созданные при помощи useRef(), присваивает input'у.
location.state.apk и inputRef компонент передаёт в функцию passURLtoInputFile, там в input.current.files она добавляет файл по ссылке.
Но вместо ожидаемого поведения, passURLtoInputFile выводит в консоль ошибку input.current is null.
Что я делаю неправильно?
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>
    );
};


FileUploader

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);
    }
};


когда я перехожу в маршрут, соответсвующий UploadUPApp, и когда выполняется passSomethingToInputFile, input равен null. Почему так происходит?
  • Вопрос задан
  • 160 просмотров
Решения вопроса 1
@wonderingpeanut
Реф нужно передавать с помощью forwardRef. // upd. я не прав

useEffect не следит за изменением ref объектов, поэтому он будет вызван один рез при первичном рендере

Если ты хочешь отправить загруженный файл через родительский компонент, best practice создать в родительском компоненте стейт и передать в ребенка хандлер изменения стейта.

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)} />
);
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы
19 апр. 2024, в 03:01
1000 руб./за проект
18 апр. 2024, в 21:56
2000 руб./за проект
18 апр. 2024, в 21:00
150 руб./за проект