为何Gutenberg区块中getEntityRecord总是使用hikeId的旧值?
问题
在编辑器中输入hikeId时,useSelect始终给getEntityRecord传入之前的旧值,而非当前输入的新值。我原本以为给useSelect设置依赖项[hikeId]后,React会在hikeId变化时重新执行它,但实际没生效。请问该怎么改代码,让distance、duration和ascend能通过当前的hikeId获取对应数据?
原代码:
import { useBlockProps } from '@wordpress/block-editor'; import { TextControl } from '@wordpress/components'; const { useSelect } = wp.data; const HikeSummaryData = ( props ) => { const { attributes: { hikeId, distance, duration, ascend }, setAttributes, } = props; const postData = useSelect(select => { return hikeId ? select('core').getEntityRecord('postType', 'post', hikeId) : undefined; }, [ hikeId ]); const determineData = (value) => { setAttributes({ hikeId: value, distance: postData && postData.meta ? postData.meta['alsnuff_hike_summary_distance_meta'] : -1, duration: postData && postData.meta ? postData.meta['alsnuff_hike_summary_duration_meta'] : -1, ascend: postData && postData.meta ? postData.meta['alsnuff_hike_summary_ascend_meta'] : -1 }); }; return ( <div {...useBlockProps()}> <fieldset> <legend>Hike ID</legend> <TextControl value={ hikeId } onChange={ determineData } /> </fieldset> <div class="card"> <footer class="card-footer"> <a href="#" class="card-footer-item">Distanz: { distance != -1 ? (Math.floor(distance / 100) / 10) + 'km' : '-' }</a> <a href="#" class="card-footer-item">Dauer: { duration != -1 ? duration + 'h' : '-' }</a> <a href="#" class="card-footer-item">Aufstieg: { ascend != -1 ? ascend + 'm' : '-' }</a> </footer> </div> </div> ); }; export default HikeSummaryData;
解决方案
核心问题是:determineData里的postData是闭包捕获的旧值——useSelect获取新数据是异步操作,不会在你修改hikeId的同步步骤里立刻更新postData。所以你在onChange里直接用postData设置其他属性,拿到的必然是旧数据。
修改思路:
- 让
onChange只负责更新hikeId,不要同步处理其他属性 - 用
useEffect监听postData的变化,当新的postData加载完成后,再更新distance、duration和ascend
修改后的代码:
import { useBlockProps } from '@wordpress/block-editor'; import { TextControl } from '@wordpress/components'; import { useEffect } from '@wordpress/element'; // 新增导入useEffect const { useSelect } = wp.data; const HikeSummaryData = ( props ) => { const { attributes: { hikeId, distance, duration, ascend }, setAttributes, } = props; const postData = useSelect(select => { return hikeId ? select('core').getEntityRecord('postType', 'post', hikeId) : undefined; }, [ hikeId ]); // 监听postData变化,同步更新属性 useEffect(() => { if (!postData?.meta) return; setAttributes({ distance: postData.meta['alsnuff_hike_summary_distance_meta'] || -1, duration: postData.meta['alsnuff_hike_summary_duration_meta'] || -1, ascend: postData.meta['alsnuff_hike_summary_ascend_meta'] || -1 }); }, [ postData, setAttributes ]); // 依赖postData和setAttributes const determineData = (value) => { // 只更新hikeId,其他属性交给useEffect处理 setAttributes({ hikeId: value }); }; return ( <div {...useBlockProps()}> <fieldset> <legend>Hike ID</legend> <TextControl value={ hikeId } onChange={ determineData } /> </fieldset> <div className="card"> {/* 修正为React规范的className */} <footer className="card-footer"> <a href="#" className="card-footer-item">Distanz: { distance !== -1 ? (Math.floor(distance / 100) / 10) + 'km' : '-' }</a> <a href="#" className="card-footer-item">Dauer: { duration !== -1 ? duration + 'h' : '-' }</a> <a href="#" className="card-footer-item">Aufstieg: { ascend !== -1 ? ascend + 'm' : '-' }</a> </footer> </div> </div> ); }; export default HikeSummaryData;
额外优化:把JSX里的class改成className,符合React语法规范,避免潜在的渲染问题。
内容的提问来源于stack exchange,提问作者Papa Mufflon
相关产品推荐
相关产品推荐

