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

如何将React自定义hook useData改造为Context实现跨组件共享运行时数据

Context改造方案

前置修复:修正原useData的runtime计算逻辑

原代码中runtime是同步计算的,而异步请求fetch的执行时间不会被统计到,所以先调整useData的逻辑:

// 修改hooks/useData.js的fetchData和useEffect部分
const fetchData = async (tableName) => { // 改为async函数
  setData([]);
  const name = TABLE_NAMES.find((name) => name === tableName);
  if (name) {
    setError(false);
    const res = await fetch(getURL(tableName));
    const data = await res.text();
    await convertToJson(data);
  } else {
    setError(true);
    toast.error("Please enter a valid query");
  }
};

useEffect(() => {
  if (!tableName) return; // 空query不执行请求
  const t0 = performance.now();
  const runQuery = async () => {
    await fetchData(tableName);
    const t1 = performance.now();
    setRuntime(t1 - t0);
    console.log("请求耗时:" + (t1 - t0) + " 毫秒");
  };
  runQuery();
}, [tableName]);

步骤1:创建DataContext

新建src/contexts/DataContext.js文件:

import React, { createContext, useContext } from 'react';
import useData from '../hooks/useData';

const DataContext = createContext(undefined);

// 提供器组件
export const DataProvider = ({ children, tableName }) => {
  const dataRes = useData(tableName);
  return (
    <DataContext.Provider value={dataRes}>
      {children}
    </DataContext.Provider>
  );
};

// 自定义取值hook,避免重复写useContext
export const useDataContext = () => {
  const context = useContext(DataContext);
  if (!context) throw new Error('useDataContext必须在DataProvider内部使用');
  return context;
};

步骤2:改造App.js接入Provider

将需要共享数据的Editor、TableSection组件用DataProvider包裹:

import React, { useState } from "react";
import "./assets/output.css";
import Footer from "./components/layouts/Footer";
import Navbar from "./components/layouts/Navbar";
import Sidebar from "./components/layouts/Sidebar";
import TableSection from "./components/table/TableSection";
import Editor from "./components/editor/Editor";
import { DataProvider } from "./contexts/DataContext"; // 引入Provider

const App = () => {
  const [query, setQuery] = useState("");
  const [value, setValue] = useState("select * from customers");

  return (
    <>
      <div className="grid grid-cols-layout-desktop grid-rows-layout-desktop bg-gray-600 h-screen">
        <Navbar />
        <Sidebar setQuery={setQuery} setValue={setValue} />
        {/* 把query作为tableName传给Provider */}
        <DataProvider tableName={query}>
          <Editor setQuery={setQuery} value={value} setValue={setValue} />
          {query ? <TableSection /> : null}
        </DataProvider>
        <Footer />
      </div>
    </>
  );
};

export default App;

步骤3:改造Editor组件获取runtime

Editor组件直接从Context取runtime,不需要自己调用useData,不会因为输入重渲染触发请求:

import React from "react";
import AceEditor from "react-ace";
import "ace-builds/src-min-noconflict/ext-language_tools";
import "ace-builds/src-min-noconflict/mode-mysql";
import "ace-builds/src-noconflict/theme-github";
import { useDataContext } from "../../contexts/DataContext"; // 引入Context hook

const Editor = ({ setQuery, value, setValue }) => {
  const { runtime } = useDataContext(); // 从Context取runtime
  const onChange = (newValue) => {
    setValue(newValue);
  };

  const onSubmit = () => {
    var Z = value.toLowerCase().slice(value.indexOf("from") + "from".length);
    setQuery(Z.split(" ")[1]);
  };

  // 可以在任意位置渲染runtime,比如按钮旁边
  return (
    <div className="col-start-2 col-end-3 row-start-2 row-end-3 m-6">
      <AceEditor
        aria-label="query editor input"
        mode="mysql"
        theme="github"
        name={Math.floor(Math.random() * 100000).toString()}
        fontSize={16}
        minLines={15}
        maxLines={10}
        width="100%"
        showPrintMargin={false}
        showGutter
        placeholder="Write your Query here..."
        editorProps={{ $blockScrolling: true }}
        setOptions={{
          enableBasicAutocompletion: true,
          enableLiveAutocompletion: true,
          enableSnippets: true,
        }}
        value={value}
        onChange={onChange}
        showLineNumbers
      />
      <div className="">
        <button
          className="bg-white text-gray-800 rounded-md font-semibold px-4 py-2 my-4 mr-4"
          onClick={onSubmit}
        >
          <i className="fas fa-play"></i> Run SQL
        </button>
        {runtime && <span className="text-white">本次查询耗时:{runtime.toFixed(2)}ms</span>}
      </div>
    </div>
  );
};

export default Editor;

步骤4:改造TableSection组件取数据

TableSection不需要再接收query参数,也不需要自己调用useData,直接从Context取数据:

import { useDataContext } from "../../contexts/DataContext";

const TableSection = () => {
  const { data, error } = useDataContext();
  // 原有渲染逻辑不变,直接用data、error即可
}

效果说明

只有当用户点击运行按钮修改query值时,才会触发useData的请求逻辑,Editor组件输入时只会修改value状态,不会触发重复请求,同时runtime和data可以在任意被DataProvider包裹的组件中直接获取。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 09:06:07