почему apiFetch вызывает Unhandled Promise Rejection: TypeError: Объект не является функцией

Вопрос или проблема

Я пытаюсь диагностировать похожую проблему, как в этом вопросе, но в том случае он не зависел от wp-api-fetch, а… я довольно уверен, что завишу.

Я получаю следующую ошибку:

[Ошибка] Непредусмотренное отклонение промиса: TypeError: Объект не является функцией. (В 'Object(_wordpress_api_fetch__WEBPACK_IMPORTED_MODULE_5__["apiFetch"])', 'Object' является экземпляром Object)

(полный трассировку стека ниже)

Я должен отметить, что я новичок как в REST API, так и в разработке плагинов ESNext/Gutenberg, так что… возможно, я упустил что-то действительно очевидное, например, запятую 🙂

Вот код:

import { __ } from '@wordpress/i18n';
import { Fragment } from '@wordpress/element';
import { TextControl } from '@wordpress/components';
import { apiFetch } from '@wordpress/api-fetch';

export default function Edit( props ) {
        const {
                attributes: { cwraggDataSourceType, cwraggDataSource,
                    cwraggLocalFile },
                setAttributes,
        } = props;

        const post_id = wp.data.select("core/editor").getCurrentPostId();

        const onChangeContent = async ( newUrl ) => {
                let localFileName = await apiFetch( {
                    path: '/cwraggb/v1/setremotedatasrc',
                    method: 'POST',
                    data: { 'url': newUrl,
                            'type': 'json',
                            'postId': post_id } } );
                ...

        };
        ...
}

Я посмотрел вывод npm run start, и, похоже, он включает зависимости в сборку:

<?php return array('dependencies' => array('wp-api-fetch', 'wp-blocks', 'wp-components', 'wp-element', 'wp-i18n', 'wp-polyfill'), 'version' => '566e4b7cb2f100542103b2b0e25aefae');

Это собирается и запускается в docker на MacOS 10.15.7.

~ % npm --version
6.14.8
~ % wp-env --version
2.1.0

Есть идеи, что вызывает эту ошибку, и/или как я могу дальше диагностировать?

Полное сообщение об ошибке:

[Ошибка] Непредусмотренное отклонение промиса: TypeError: Объект не является функцией. (В 'Object(_wordpress_api_fetch__WEBPACK_IMPORTED_MODULE_5__["apiFetch"])', 'Object' является экземпляром Object)
    dispatchException (wp-polyfill.js:7017)
    invoke (wp-polyfill.js:6738)
    asyncGeneratorStep (cwra-google-graph-block-admin.js:250)
    _next (cwra-google-graph-block-admin.js:272)
    (анонимная функция) (cwra-google-graph-block-admin.js:279)
    Promise
    (анонимная функция) (cwra-google-graph-block-admin.js:268)
    callCallback (react-dom.js:341)
    dispatchEvent
    invokeGuardedCallbackDev (react-dom.js:391)
    invokeGuardedCallback (react-dom.js:448)
    invokeGuardedCallbackAndCatchFirstError (react-dom.js:462)
    executeDispatch (react-dom.js:594)
    executeDispatchesInOrder (react-dom.js:616)
    executeDispatchesAndRelease (react-dom.js:719)
    forEach
    forEachAccumulated (react-dom.js:699)
    runEventsInBatch (react-dom.js:744)
    runExtractedPluginEventsInBatch (react-dom.js:875)
    handleTopLevel (react-dom.js:6026)
    dispatchEventForPluginEventSystem (react-dom.js:6121)
    dispatchEvent (react-dom.js:6150)
    dispatchEvent
    unstable_runWithPriority (react.js:2820)
    discreteUpdates$1 (react-dom.js:21810)
    discreteUpdates (react-dom.js:2357)
    dispatchDiscreteEvent (react-dom.js:6104)
    dispatchDiscreteEvent

Основная проблема заключается в недопонимании того, как работают асинхронные функции.

const onChangeContent = async ( newUrl ) => {

onChangeContent не является функцией/асинхронной функцией. Это объект, в частности Promise.

async и await являются просто сокращением. Эти 2 функции идентичны:

async function foo() {
   return 1
}

function foo() {
   return Promise.resolve(1)
}

Поскольку React ожидает функцию, он вместо этого получает Promise и терпит неудачу.

Кроме того, если ваш вызов apiFetch завершится неудачно по какой-либо причине, нет обработчика ошибок для возвращенного промиса.

Не только это, но и код был помещен напрямую в компонент React. Я настоятельно не рекомендую это. Вместо этого используйте useEffect и комбинируйте его с локальным состоянием, если вам нужно передать информацию обратно в компонент. useEffect вызывает функцию после того, как компонент React был отрисован, поэтому это не блокирует выполнение. Это также предотвращает повторные ненужные вызовы.

Например, вот мы получаем посты, отображаем статус загрузки и т.д.:

import React from 'react';
import apiFetch from '@wordpress/api-fetch';
import { useSelect } from '@wordpress/data';
import { useState, useEffect, useRef } from '@wordpress/element';
import { addQueryArgs } from '@wordpress/url';

function PostList() {
    // мы загружаем посты?
    const [ isLoading, setLoading ] = useState( true );
    // посты, которые мы отображаем
    const [ posts, setPosts ] = useState([]);
    // мы не хотим обновлять состояние для размонтированного компонента
    const isStillMounted = useRef();

    // получаем посты
    useEffect(() => {
        isStillMounted.current = true;
        apiFetch( {
            path: addQueryArgs( `/wp/v2/posts`, { } ),
        } )
            .then( ( data ) => {
                if ( isStillMounted.current ) {
                    setPosts( data );
                    setLoading( false );
                }
            } )
            .catch( () => {
                if ( isStillMounted.current ) {
                    setPosts( [] );
                    setLoading( false );
                }
            } );
    }, [setPosts, setLoading, isStillMounted]);

    if ( isLoading ) {
        return <div>Загрузка...</div>;
    }

    return <div>{ .. используйте переменную posts здесь ... }</div>

}

Имейте в виду, что это не специфические для редактора блоков вещи, это общие вещи JavaScript React.

Примечание:

  • Что у нас есть catchClause для вызова apiFetch
  • Что вызов fetch является побочным эффектом, поэтому он был перемещен в хук useEffect
  • Единственные изменения, затрагивающие эффект за пределами функции, касаются обновления состояния
  • Этот эффект не будет снова вызван, если 3 зависимости не изменятся или если новый экземпляр этого компонента не будет смонтирован
  • Состояние загрузки было добавлено
  • Мы использовали useRef, чтобы отслеживать, смонтирован ли компонент. Если ваш блок был удален до завершения вызова API, это предотвращает ошибку

Хотя я все еще не понимаю ESNext/JavaScript достаточно хорошо, чтобы знать, почему это решение, оказалось, что проблема действительно относилась к категории “пропущена запятая” — конкретно, у меня не могло быть скобок в импорте. Так что

import { __ } from '@wordpress/i18n';
import { Fragment } from '@wordpress/element';
import { TextControl } from '@wordpress/components';
import apiFetch from '@wordpress/api-fetch;

решила проблему. Тем не менее, ответ Тома стоит прочитать также (и включает в себя этот нюанс).

Ответ или решение

Проблема, с которой вы столкнулись, вероятно, связана с тем, как вы импортируете компонент apiFetch из библиотеки WordPress. Сообщение об ошибке, с которым вы столкнулись – "Unhandled Promise Rejection: TypeError: Object is not a function", указывает на то, что вместо ожидаемой функции вы получаете объект. Давайте разберем основные аспекты проблемы и возможные пути ее решения.

1. Неправильный импорт

Ваша ошибка, в первую очередь, может быть связана с тем, как импортируется apiFetch. В первоначальном коде вы использовали синтаксис, который указывает на именованный импорт, вместо стандартного импорта по умолчанию. Вот пример неправильного импорта:

import { apiFetch } from '@wordpress/api-fetch'; // Это неверно

Если функция apiFetch экспортируется как стандартный (по умолчанию) экспорт, то правильным будет:

import apiFetch from '@wordpress/api-fetch'; // Это правильно

2. Понимание асинхронных функций и Promises

Другим аспектом проблемы является использование асинхронной функции. При объявлении onChangeContent с использованием async, вы возвращаете Promise, который не является ожидаемым результатом, когда React ожидает функцию. Таким образом, вы можете столкнуться с трудностями, если React пытается вызвать эту функцию. Вместо этого попробуйте следующее:

const onChangeContent = (newUrl) => {
    apiFetch({
        path: '/cwraggb/v1/setremotedatasrc',
        method: 'POST',
        data: { 
            'url': newUrl,
            'type': 'json',
            'postId': post_id
        }
    }).then((localFileName) => {
        // обработка успешного ответа
    }).catch((error) => {
        console.error("Ошибка при вызове apiFetch:", error);
        // обработка ошибок
    });
};

3. Использование хуков React и управление состоянием

Ваш код должен корректно обрабатывать асинхронные операции. Используйте useEffect для выполнения побочных эффектов. Например:

import React, { useEffect, useState } from 'react';
import apiFetch from '@wordpress/api-fetch';

const Edit = (props) => {
    const { 
        attributes: { cwraggDataSourceType, cwraggDataSource, cwraggLocalFile }, 
        setAttributes 
    } = props;

    const post_id = wp.data.select("core/editor").getCurrentPostId();
    const [state, setState] = useState({ loading: false, data: null });

    const onChangeContent = (newUrl) => {
        setState({ loading: true });

        apiFetch({
            path: '/cwraggb/v1/setremotedatasrc',
            method: 'POST',
            data: { 
                'url': newUrl,
                'type': 'json',
                'postId': post_id 
            }
        }).then((localFileName) => {
            // обновление состояния по успешному завершению
            setState({ loading: false, data: localFileName });
        }).catch((error) => {
            console.error("Ошибка при вызове apiFetch:", error);
            setState({ loading: false, data: null });
        });
    };

    return (
        <Fragment>
            {/* ваш JSX-код */}
        </Fragment>
    );
};

Заключение

Ваша проблема с apiFetch связана с неправильным импортом и управлением асинхронными процессами. Убедитесь, что вы используете правильный тип импорта и обрабатываете асинхронные операции согласно стандартам React. Внедрение хуков, таких как useEffect, улучшит структуру вашего кода и упростит обработку данных.

Если вы будете следовать указанным исправлениям и рекомендациям, то сможете избежать подобной ошибки в будущем и улучшить вашу разработку в среде WordPress и React.

Оцените материал
Добавить комментарий

Капча загружается...