You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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>
  );
};

关键优化点

  1. 用useCallback包裹preloadSurvey,确保函数引用稳定,避免组件渲染时触发不必要的重渲染
  2. 用useRef记录上一次的调研ID,避免重复预加载同一个资源
  3. 弹窗关闭时清空预加载状态,避免下次打开弹窗显示旧数据

内容的提问来源于stack exchange,提问作者esraa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 07:02:37