Вопрос или проблема
Я попытался использовать useSelect в колбэке события, как описано в руководстве блоков, однако я получаю undefined, так как значение получено позже, чем мне нужно. Я полагаю, что мне нужна какая-то дополнительная асинхронная функция, чтобы поймать значение, когда оно будет получено, или существует какой-то другой механизм, который не описан здесь?
Обратите внимание, что я не использую useSelect в классическом режиме с колбэком, потому что значение не может быть получено при обновлении компонента, а только при действии пользователя.
Этот код должен проверять, когда выбран определенный тип публикации, затем получить его выделенное изображение, в противном случае предоставить пользователю возможность выбрать изображение. В настоящее время я только записываю в консоль, потому что отлаживаю это.
SingleImage
— это компонент, который позволяет пользователю выбрать и заменить изображение.
import { useBlockProps, URLInput } from '@wordpress/block-editor';
import SingleImage from '../../components/singleImage.jsx'
import { useSelect } from '@wordpress/data';
import './editor.scss';
export default function Edit({ attributes, setAttributes }) {
const { getEntityRecord } = useSelect('core');
const urlInputSetter = (url, post) => {
if (post && ! attributes.imgID) {
if (post.type === 'virtualne-izlozbe') {
const record = getEntityRecord('postType', 'virtualne-izlozbe', post.id );
if (record)
console.log(record.featured_media);
else
console.log('empty');
}
}
setAttributes( { link: url, postTitle: (post && post.title) } )
}
return (
<div { ...useBlockProps() }>
<URLInput
value={ attributes.link }
onChange={ urlInputSetter }
label="Ссылка на публикацию"
className="post-link"
/>
{ attributes.postTitle && <div className="post-title">Изображение ведет на публикацию { attributes.postTitle }</div> }
<SingleImage
imgID={attributes.imgID}
imgURL={attributes.imgURL}
alt="Изображение, которое ведет на публикацию"
setImgIdAndUrl={(imgID, imgURL) => setAttributes({imgID, imgURL})}
title="Выбрать изображение"
help="Изображение, которое ведет на публикацию"
/>
</div>
);
}
В документации, которую вы опубликовали, сказано:
Не используйте useSelect таким образом, когда вызываете селекторы в функции рендера, потому что ваш компонент не будет перерисовываться при изменении данных.
import { useSelect } from '@wordpress/data';
function Paste( { children } ) {
const { getSettings } = useSelect( 'my-shop' );
function onPaste() {
// Сделать что-то с настройками.
const settings = getSettings();
}
return <div onPaste={ onPaste }>{ children }</div>;
}
Что в основном соответствует тому, как вы использовали, и поэтому ваш компонент перерисовывается при изменении.
Вы можете использовать хуки react (useState, useEffect), чтобы решить эту проблему. См. код ниже. Я прокомментировал код, чтобы объяснить изменения. (код не тестировался)
import { useBlockProps, URLInput } from '@wordpress/block-editor';
import SingleImage from '../../components/singleImage.jsx';
import { useSelect } from '@wordpress/data';
const { useState, useEffect } = wp.element;
import './editor.scss';
export default function Edit({ attributes, setAttributes }) {
// Подготовка состояния
const [post, setPost] = useState();
// useSelect с колбэком, обратите внимание на третий параметр
const record = useSelect(
(select) => {
return select('core').getEntityRecords(
'postType',
'virtualne-izlozbe',
post.id
);
},
// Это массив зависимостей, каждый раз, когда состояние
// "post" изменяется, будет вызван колбэк useSelect.
[post]
);
// Выполнение действий при изменении состояния
useEffect(() => {
// изменение значения record, сделать что-то
if (record) {
console.log(record.featured_media);
} else {
console.log('пусто');
}
// Массив зависимостей, каждый раз, когда record изменяется, колбэк useEffect
// будет вызван
}, [record]);
const urlInputSetter = (url, urlPost) => {
if (urlPost && !attributes.imgID) {
if (urlPost.type === 'virtualne-izlozbe') {
// Изменение состояния, чтобы колбэк useSelect был вызван
setPost(urlPost);
}
}
setAttributes({ link: url, postTitle: post && post.title });
};
return (
<div {...useBlockProps()}>
<URLInput
value={attributes.link}
onChange={urlInputSetter}
label="Ссылка на публикацию"
className="post-link"
/>
{attributes.postTitle && (
<div className="post-title">
Изображение ведет на публикацию {attributes.postTitle}
</div>
)}
<SingleImage
imgID={attributes.imgID}
imgURL={attributes.imgURL}
alt="Изображение, которое ведет на публикацию"
setImgIdAndUrl={(imgID, imgURL) =>
setAttributes({ imgID, imgURL })
}
title="Выбрать изображение"
help="Изображение, которое ведет на публикацию"
/>
</div>
);
}
Ответ или решение
Решение проблемы использования useSelect
в колбэке события в React
При работе с хук-формами в среде WordPress, разработчики нередко сталкиваются с проблемой, когда значения, получаемые с помощью useSelect
, не обновляются вовремя, особенно в случае обработки событий, таких как ввод пользователя. Рассмотрим ваш случай, в котором вы пытаетесь использовать useSelect
для получения записи поста и его актуального значения, что приводит к неопределённому результату. Вот подробный подход к решению этой проблемы.
Контекст задачи
Ваш код использует компонент <URLInput />
для обновления ссылки на пост, который по выбору поста должен извлекать изображение поста, если тип поста 'virtualne-izlozbe'
. Однако getEntityRecord
из useSelect
вызывается внутри колбэка, что будет иметь место только при первом рендере, что приводит к тому, что значение в логах оказывается неопределенным.
Проблемы с текущей реализацией
-
Использование
useSelect
вне его контекста рендеринга:useSelect
должен вызываться в самом компоненте или в колбэке, который подписан на соответствующие изменения состояния. В вашем случае вы вызываете его в колбэке события, что приводит к тому, что изменений состояния не происходит. -
Асинхронный доступ к данным: Из-за того что запрос к данным может занять некоторое время, вызов
setState
для получения значения может привести к несоответствиям.
Оптимизированное решение
Чтобы правильно реализовать обновление ваших данных и избежать описанных проблем, используйте состояние и эффектные хуки (useState
, useEffect
).
Пример кода с объяснениями:
import { useBlockProps, URLInput } from '@wordpress/block-editor';
import SingleImage from '../../components/singleImage.jsx';
import { useSelect } from '@wordpress/data';
const { useState, useEffect } = wp.element;
import './editor.scss';
export default function Edit({ attributes, setAttributes }) {
const [post, setPost] = useState();
// Используем useSelect для получения записи поста
const record = useSelect(
(select) => {
return select('core').getEntityRecord('postType', 'virtualne-izlozbe', post ? post.id : null);
},
[post] // Зависимость от состояния post
);
// Используем useEffect для обработки изменений записи
useEffect(() => {
if (record) {
console.log(record.featured_media);
} else {
console.log('empty');
}
}, [record]); // Срабатывает при изменении record
const urlInputSetter = (url, urlPost) => {
if (urlPost && !attributes.imgID) {
if (urlPost.type === 'virtualne-izlozbe') {
setPost(urlPost); // Сохраняем пост в состояние
}
}
setAttributes({ link: url, postTitle: urlPost ? urlPost.title : undefined});
};
return (
<div {...useBlockProps()}>
<URLInput
value={attributes.link}
onChange={urlInputSetter}
label="Link na objavu"
className="post-link"
/>
{attributes.postTitle && (
<div className="post-title">
Ссылка ведёт на объект {attributes.postTitle}
</div>
)}
<SingleImage
imgID={attributes.imgID}
imgURL={attributes.imgURL}
alt="Изображение для ссылки"
setImgIdAndUrl={(imgID, imgURL) => setAttributes({ imgID, imgURL })}
title="Выберите изображение"
help="Изображение для ссылки"
/>
</div>
);
}
Объяснение изменений:
-
Использование состояния: Добавление состояния
post
позволяет вам сохранять данные поста и повторно их использовать при необходимости. -
Изменение
useSelect
: ТеперьuseSelect
следит за изменением состоянияpost
, что позволяет компоненту React повторно рендерить, когда данные о посте обновляются. -
Эффекты:
useEffect
отслеживает изменения вrecord
, позволяя асинхронно обрабатывать данные без блокировки основного потока выполнения.
Заключение
Данное решение устраняет проблемы с асинхронностью и обеспечит корректное получение данных. Использование хуков useState
и useEffect
в сочетании с useSelect
позволяет эффективно управлять данными и состоянием, соответствуя лучшим практикам разработки в React и WordPress.