Вопрос или проблема
Я пытаюсь диагностировать похожую проблему, как в этом вопросе, но в том случае он не зависел от 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.
Примечание:
- Что у нас есть
catch
Clause для вызова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.