React Chrome扩展:Storage值无法在组件中显示的问题求助
基于Udemy教程开发的React Chrome扩展中,通过右键菜单选项保存到Storage的naviID值,无法在popup组件的对应输入框中显示。以下是相关代码及问题分析:
核心问题原因
存储与读取的结构不匹配:在Storage.ts的set方法中,将数据包裹在values对象下存储,但get方法直接从根对象读取目标字段,导致无法获取到保存的值。
相关代码
Storage.ts
export interface LocalStorage { shopURL?: string; naviID?: string; } export type LocalStorageKeys = keyof LocalStorage; export function setStoredShopURL(shopURL: string): Promise<void> { const values: LocalStorage = { shopURL, }; return new Promise((resolve) => { chrome.storage.local.set({ values }, () => { resolve(); }); }); } export function getStoredShopURL(): Promise<string> { const keys: LocalStorageKeys[] = ["shopURL"]; return new Promise((resolve) => { chrome.storage.local.get(keys, (res: LocalStorage) => resolve(res.shopURL ?? "") ); }); } export function setStoredNaviID(naviID: string): Promise<void> { console.log(`naviID inside setStoredNaviID: ${naviID}`); const values: LocalStorage = { naviID, }; return new Promise((resolve) => { chrome.storage.local.set({ values }, () => { resolve(); }); }); } export function getStoredNaviID(): Promise<string> { const keys: LocalStorageKeys[] = ["naviID"]; return new Promise((resolve) => { chrome.storage.local.get(keys, (res: LocalStorage) => resolve(res.naviID ?? "") ); }); }
popup.tsx
import * as React from "react"; import { createRoot } from "react-dom/client"; import IconButton from "@mui/material/IconButton"; import OutlinedInput from "@mui/material/OutlinedInput"; import InputLabel from "@mui/material/InputLabel"; import InputAdornment from "@mui/material/InputAdornment"; import FormControl from "@mui/material/FormControl"; import ClearIcon from "@mui/icons-material/Clear"; import FormHelperText from "@mui/material/FormHelperText"; import Container from "@mui/material/Container"; import Typography from "@mui/material/Typography"; import { useEffect, useState } from "react"; import "./popup.css"; import { getCurrentTab } from "../background/background"; import { correctUserUrlInput } from "../utils/helperFunctions"; import { getStoredNaviID } from "../utils/storage"; function App() { const [shopURL, setShopURL] = useState(""); const [naviID, setNaviID] = useState(""); const handleClickDeleteUserInput = () => { setShopURL(""); }; useEffect(() => { getCurrentTab().then((tab) => { const shortenedURL = correctUserUrlInput(tab.url); console.log(shortenedURL); setShopURL(shortenedURL); }); getStoredNaviID().then((naviID) => { console.log(naviID); setNaviID(naviID); }); }, []); return ( <> <Typography variant="h4">React URL Picker</Typography> <Container maxWidth="xl"> <FormControl fullWidth variant="outlined" margin="normal"> <InputLabel htmlFor="outlined-adornment-url">Shop URL</InputLabel> <OutlinedInput id="outlined-adornment-url" type="text" value={shopURL} onChange={(event) => setShopURL(event.target.value)} endAdornment={ <InputAdornment position="end"> <IconButton aria-label="delete user input" onClick={handleClickDeleteUserInput} edge="end" > <ClearIcon /> </IconButton> </InputAdornment> } label="url" /> <FormHelperText id="my-helper-text-url"> Ihr müsst euch auf einer Shopseite befinden, damit diese automatisch ausgelesen wird. </FormHelperText> </FormControl> <FormControl fullWidth variant="outlined" margin="normal"> <InputLabel htmlFor="outlined-naviID">Navi-ID</InputLabel> <OutlinedInput id="outlined-naviID" type="text" value={naviID} onChange={(event) => setNaviID(event.target.value)} label="naviID" /> <FormHelperText id="my-helper-text-naviID"> Navi-ID der aktuellen Seite </FormHelperText> </FormControl> </Container> </> ); } const container = document.createElement("div"); document.body.appendChild(container); const root = createRoot(container); root.render(<App />);
background.ts
import { getNaviID } from "../utils/helperFunctions"; import { setStoredNaviID } from "../utils/storage"; export async function getCurrentTab() { let queryOptions = { active: true, currentWindow: true }; let [tab] = await chrome.tabs.query(queryOptions); return tab; } chrome.runtime.onInstalled.addListener(() => { chrome.contextMenus.create({ title: "NaviID speichern", id: "saveNaviId", }); }); chrome.contextMenus.onClicked.addListener((clickData) => { if (clickData.menuItemId == "saveNaviId") { console.log("CLICK!"); getCurrentTab().then((tab) => { console.log(tab.url); const naviID = getNaviID(tab.url); console.log(naviID); setStoredNaviID(naviID[0]); }); } });
manifest.json
{ "manifest_version": 3, "name": "React URL Picker", "description": "React URL Picker for the Shopline Content Team", "version": "1.0.0", "icons": { "16": "cntnt.png", "48": "cntnt.png", "128": "cntnt.png" }, "action": { "default_popup": "popup.html", "default_title": "React URL Picker", "default_icon": "cntnt.png" }, "options_page": "options.html", "background": { "service_worker": "background.js" }, "content_scripts": [ { "matches": ["<all_urls>"], "js": ["contentScript.js"] } ], "permissions": ["storage", "tabs", "activeTab", "contextMenus"] }
修复方案
有两种可行的修复方式,任选其一即可:
方案1:修改存储逻辑,直接存储字段(推荐)
调整setStoredShopURL和setStoredNaviID方法,去掉values包裹,直接存储目标字段:
export function setStoredShopURL(shopURL: string): Promise<void> { return new Promise((resolve) => { chrome.storage.local.set({ shopURL }, () => { resolve(); }); }); } export function setStoredNaviID(naviID: string): Promise<void> { console.log(`naviID inside setStoredNaviID: ${naviID}`); return new Promise((resolve) => { chrome.storage.local.set({ naviID }, () => { resolve(); }); }); }
此时原有的get方法可以正常工作,因为存储的键就是shopURL和naviID,与get方法读取的键一致。
方案2:修改读取逻辑,匹配存储结构
调整getStoredShopURL和getStoredNaviID方法,从values对象中读取目标字段:
export function getStoredShopURL(): Promise<string> { return new Promise((resolve) => { chrome.storage.local.get("values", (res) => { resolve(res?.values?.shopURL ?? ""); }); }); } export function getStoredNaviID(): Promise<string> { return new Promise((resolve) => { chrome.storage.local.get("values", (res) => { resolve(res?.values?.naviID ?? ""); }); }); }
这种方式不需要修改set方法,只需让读取逻辑匹配存储的嵌套结构。
额外检查点
- 确认
getNaviID函数返回的数组是否包含有效值,可通过background中的console.log(naviID)输出验证。 - 打开Chrome扩展的开发者工具(右键popup选择“检查”),查看
getStoredNaviID的console.log(naviID)输出,确认是否为空或未定义。
内容的提问来源于stack exchange,提问作者Stefan Strathmeier
相关产品推荐
相关产品推荐

