Chrome扩展中如何从Popup向主窗口传递React Context数据
问题解答
首先明确:默认情况下,Chrome Popup里的React Context无法直接被新窗口中的组件访问。因为Chrome的Popup和通过react-new-window创建的新窗口是完全独立的JavaScript执行环境,各自拥有独立的React组件树和Context实例,数据不会自动同步。
下面提供两种可行的实现方案,适配Chrome扩展的场景:
方案一:使用Chrome Storage API(推荐)
Chrome扩展的chrome.storage API可以在Popup、背景页、新窗口等扩展组件之间安全共享数据,同时支持持久化存储,是这类场景的最优解。
步骤1:修改Context,同步Chrome Storage
更新ScheduleContext.js,让Context的状态与Chrome Storage双向同步:
import React, { createContext, useContext, useState, useEffect } from 'react'; const ScheduleContext = createContext(); export const useScheduleContext = () => useContext(ScheduleContext); export const ScheduleProvider = ({ children }) => { const [selectedRowData, setSelectedRowData] = useState(null); // 初始化时从Storage读取数据 useEffect(() => { const loadInitialData = async () => { const storedData = await chrome.storage.local.get('selectedRowData'); if (storedData.selectedRowData) { setSelectedRowData(storedData.selectedRowData); } }; loadInitialData(); // 监听Storage变化,自动同步到Context const handleStorageUpdate = (changes, area) => { if (area === 'local' && changes.selectedRowData?.newValue) { setSelectedRowData(changes.selectedRowData.newValue); } }; chrome.storage.onChanged.addListener(handleStorageUpdate); return () => { chrome.storage.onChanged.removeListener(handleStorageUpdate); }; }, []); // 封装状态更新方法,同时写入Storage const updateSelectedRow = async (data) => { setSelectedRowData(data); await chrome.storage.local.set({ selectedRowData: data }); }; return ( <ScheduleContext.Provider value={{ selectedRowData, setSelectedRowData: updateSelectedRow }}> {children} </ScheduleContext.Provider> ); };
步骤2:配置扩展权限
在manifest.json中添加storage权限:
{ "permissions": ["storage"] }
步骤3:原有组件无需大幅修改
MainOrderList.js的handleRowClick逻辑保持不变,调用setSelectedRowData时会自动同步到Storage:const handleRowClick = (params) => { const rowData = params.row; setSelectedRowData(rowData); };Schedules.js的逻辑也无需修改,Context会自动从Storage同步最新数据,useEffect会在数据更新时触发回调。
方案二:使用Window.postMessage(实时临时通信)
如果不需要持久化数据,仅需实时传递一次数据,可以用浏览器的postMessage API实现窗口间通信。
步骤1:Popup中保存新窗口引用并发送数据
假设Popup中负责打开新窗口的组件:
import React, { useState } from 'react'; import NewWindow from 'react-new-window'; import MainOrderList from './MainOrderList'; import { useScheduleContext } from 'src/context/ScheduleContext'; function PopupContainer() { const [newWindowInstance, setNewWindowInstance] = useState(null); const { setSelectedRowData } = useScheduleContext(); const handleRowClick = (rowData) => { setSelectedRowData(rowData); // 向新窗口发送数据 if (newWindowInstance?.window) { newWindowInstance.window.postMessage( { type: 'SELECTED_ROW', data: rowData }, chrome.runtime.getURL('/') // 限制接收方为扩展内页面,提升安全性 ); } }; return ( <div> <MainOrderList onRowClick={handleRowClick} /> <NewWindow onOpen={(window) => setNewWindowInstance({ window })}> <Schedules /> </NewWindow> </div> ); }
步骤2:新窗口组件监听消息
更新Schedules.js,监听message事件并更新Context:
import React, { useEffect } from 'react'; import { useScheduleContext } from 'src/context/ScheduleContext'; export default function Schedules() { const { selectedRowData, setSelectedRowData } = useScheduleContext(); useEffect(() => { const handleIncomingMessage = (event) => { // 验证消息来源,避免恶意数据 if (event.origin !== chrome.runtime.getURL('/')) return; if (event.data.type === 'SELECTED_ROW') { setSelectedRowData(event.data.data); } }; window.addEventListener('message', handleIncomingMessage); return () => window.removeEventListener('message', handleIncomingMessage); }, [setSelectedRowData]); useEffect(() => { if (selectedRowData) { console.log('Selected Row Data Updated:', selectedRowData); // 处理数据逻辑 } }, [selectedRowData]); return <div>Schedule Component</div>; }
内容的提问来源于stack exchange,提问作者Chandima Dilshan
相关产品推荐
相关产品推荐

