React Context Provider中sql.js数据库实例为Null的问题排查
解决React Context中sql.js数据库实例未就绪导致的
TypeError: Cannot read properties of null (reading 'exec')问题 问题根源
你的代码核心问题是:initSqlJs是异步操作,需要时间完成初始化,但App组件在首次渲染时直接调用sqliteDB.exec,此时sqliteDB还是初始的null状态,自然会触发报错。
修复方案
通过添加加载状态控制组件渲染时机+监听数据库实例状态变化来解决,具体修改如下:
1. 优化SQLiteContext,添加加载状态
修改SQLiteContext.js,新增加载状态标记数据库是否初始化完成,只有初始化完成后才渲染子组件:
import React, { createContext, useEffect, useState } from "react"; export const SQLiteContext = createContext(); const SQLiteProvider = ({ children }) => { const [SQL, setSQL] = useState(null); const [sqliteDB, setSqliteDB] = useState(null); const [isLoading, setIsLoading] = useState(true); // 标记数据库是否在加载中 useEffect(() => { initSqlJs({ locateFile: file => `/js/${file}` }) .then((aSQL) => { setSQL(aSQL); setSqliteDB(new aSQL.Database()); setIsLoading(false); // 初始化完成,更新加载状态 }) .catch((err) => { console.error("SQLite初始化失败:", err); setIsLoading(false); }); }, []); return ( <SQLiteContext.Provider value={{ SQL, sqliteDB, isLoading }}> {/* 加载中显示占位内容,避免子组件访问未就绪的数据库 */} {isLoading ? <div>数据库加载中...</div> : children} </SQLiteContext.Provider> ); }; export { SQLiteProvider };
2. 修改App组件,监听数据库实例状态
在App.js中,通过useEffect监听sqliteDB的变化,确保只有当实例存在时才执行查询:
import React, { useContext, useEffect, useState } from "react"; import { SQLiteContext } from "../contexts/SQLiteContext"; export default function App() { const { sqliteDB } = useContext(SQLiteContext); const [queryResult, setQueryResult] = useState(null); useEffect(() => { if (!sqliteDB) return; // 实例未就绪时直接返回 try { // 先确保目标表存在,避免查询不存在的表报错 sqliteDB.exec("CREATE TABLE IF NOT EXISTS hello (id INTEGER PRIMARY KEY, name TEXT)"); // 执行查询 const res = sqliteDB.exec("SELECT * FROM hello"); setQueryResult(JSON.stringify(res)); } catch (err) { console.error("查询执行失败:", err); } }, [sqliteDB]); // 依赖sqliteDB,实例变化时重新执行 return ( <> <div>test</div> {queryResult && <div>查询结果:{queryResult}</div>} </> ); }
为什么之前的尝试没解决问题?
- 合并到单个文件:异步初始化的本质问题没解决,组件渲染时机还是早于数据库实例就绪
- 在initSqlJs回调中执行查询:如果是在Provider的回调里执行,组件依然会先渲染并尝试访问null的实例;如果是在组件内直接写,没通过
useEffect监听状态变化 - 用window.db全局初始化:全局变量虽然能拿到实例,但React组件的渲染时机无法保证晚于初始化完成,而且不符合Context的状态管理规范
额外优化建议
- 可以在Provider的
initSqlJs回调中预创建常用表,避免每个组件重复执行建表语句 - 把数据库操作(如查询、插入)封装成方法放到Context的value中,组件直接调用封装好的方法,降低耦合度
内容的提问来源于stack exchange,提问作者pg0
相关产品推荐
相关产品推荐

