如何在onClick触发时使用useLazyLoadQuery?Relay查询问题求助
解决Relay在通知弹窗中加载数据的问题
方案一:用useLazyLoadQuery实现点击触发查询
useLazyLoadQuery作为React Hook必须在组件顶层调用,不能直接放在onClick这类事件回调里。我们可以把弹窗拆成独立组件,通过状态控制弹窗的显示/隐藏,当弹窗挂载时自动执行查询。
代码示例
通知列表组件
import { useState } from 'react'; import NotificationModal from './NotificationModal'; const NotificationList = ({ notifications }) => { const [selectedSurveyId, setSelectedSurveyId] = useState(null); const handleNotificationClick = (surveyId) => { setSelectedSurveyId(surveyId); }; return ( <div> {notifications.map(notification => ( <div key={notification.id} onClick={() => handleNotificationClick(notification.surveyId)} style={{ cursor: 'pointer', padding: '8px', borderBottom: '1px solid #eee' }} > {notification.content} </div> ))} {selectedSurveyId && ( <NotificationModal surveyId={selectedSurveyId} onClose={() => setSelectedSurveyId(null)} /> )} </div> ); };
弹窗组件(使用useLazyLoadQuery)
import { useLazyLoadQuery, graphql } from 'react-relay'; const SURVEY_QUERY = graphql` query NotificationModalSurveyQuery($surveyId: ID!) { survey(id: $surveyId) { id title questions { id text } } } `; const NotificationModal = ({ surveyId, onClose }) => { // 在组件顶层调用Hook,弹窗挂载时自动发起查询 const data = useLazyLoadQuery( SURVEY_QUERY, { surveyId }, { fetchPolicy: 'store-or-network' } // 优先从缓存取数据,无缓存再请求网络 ); if (!data.survey) { return ( <div className="modal-overlay"> <div className="modal">加载中...</div> </div> ); } return ( <div className="modal-overlay"> <div className="modal"> <button onClick={onClose} style={{ float: 'right' }}>关闭</button> <h2>{data.survey.title}</h2> <div style={{ marginTop: '20px' }}> {data.survey.questions.map(question => ( <div key={question.id} style={{ margin: '8px 0' }}> {question.text} </div> ))} </div> </div> </div> ); };
方案二:优化usePreloadedQuery避免频繁重渲染
如果坚持用预加载方案,频繁重渲染通常是因为loadQuery函数引用不稳定、重复发起预加载请求导致的。可以通过以下方式优化:
代码示例
通知列表组件(优化预加载逻辑)
import { useState, useCallback, useRef } from 'react'; import { usePreloadedQuery, loadQuery, graphql } from 'react-relay'; const SURVEY_QUERY = graphql` query NotificationModalSurveyQuery($surveyId: ID!) { survey(id: $surveyId) { id title questions { id text } } } `; const NotificationList = ({ notifications, environment }) => { const [preloadedQuery, setPreloadedQuery] = useState(null); const [selectedSurveyId, setSelectedSurveyId] = useState(null); const prevSurveyIdRef = useRef(null); // 用useCallback稳定函数引用,避免组件渲染时重复创建 const preloadSurvey = useCallback((surveyId) => { // 避免重复预加载同一个调研ID if (surveyId === prevSurveyIdRef.current) return; prevSurveyIdRef.current = surveyId; const queryRef = loadQuery(environment, SURVEY_QUERY, { surveyId }); setPreloadedQuery(queryRef); }, [environment]); const handleNotificationClick = (surveyId) => { preloadSurvey(surveyId); setSelectedSurveyId(surveyId); }; const handleClose = () => { setSelectedSurveyId(null); setPreloadedQuery(null); prevSurveyIdRef.current = null; }; return ( <div> {notifications.map(notification => ( <div key={notification.id} onClick={() => handleNotificationClick(notification.surveyId)} style={{ cursor: 'pointer', padding: '8px', borderBottom: '1px solid #eee' }} > {notification.content} </div> ))} {selectedSurveyId && preloadedQuery && ( <NotificationModal preloadedQuery={preloadedQuery} onClose={handleClose} /> )} </div> ); }; // 弹窗组件(使用usePreloadedQuery) const NotificationModal = ({ preloadedQuery, onClose }) => { const data = usePreloadedQuery(SURVEY_QUERY, preloadedQuery); if (!data.survey) { return ( <div className="modal-overlay"> <div className="modal">加载中...</div> </div> ); } return ( <div className="modal-overlay"> <div className="modal"> <button onClick={onClose} style={{ float: 'right' }}>关闭</button> <h2>{data.survey.title}</h2> <div style={{ marginTop: '20px' }}> {data.survey.questions.map(question => ( <div key={question.id} style={{ margin: '8px 0' }}> {question.text} </div> ))} </div> </div> </div> ); };
关键优化点
- 用
useCallback包裹preloadSurvey,确保函数引用稳定,避免组件渲染时触发不必要的重渲染 - 用
useRef记录上一次的调研ID,避免重复预加载同一个资源 - 弹窗关闭时清空预加载状态,避免下次打开弹窗显示旧数据
内容的提问来源于stack exchange,提问作者esraa
相关产品推荐
相关产品推荐

