Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Проблема синхронизации вкладок
Когда пользователь открывает несколько вкладок одного приложения, данные могут быть несинхронизированы. Пользователь меняет настройки в одной вкладке, но в другой вкладке они не обновляются.
Способ 1: Storage Event (localStorage/sessionStorage)
// Неправильно: изменения в одной вкладке не видны в другой
function Settings() {
const [theme, setTheme] = useState("light");
const handleThemeChange = (newTheme) => {
setTheme(newTheme);
localStorage.setItem("theme", newTheme);
// Другие вкладки не узнают об этом!
};
return <button onClick={() => handleThemeChange("dark")}>Switch</button>;
}
// Правильно: слушаем Storage Event
function Settings() {
const [theme, setTheme] = useState(() => {
return localStorage.getItem("theme") || "light";
});
useEffect(() => {
const handleStorageChange = (event) => {
if (event.key === "theme" && event.newValue) {
setTheme(event.newValue);
}
};
window.addEventListener("storage", handleStorageChange);
return () => {
window.removeEventListener("storage", handleStorageChange);
};
}, []);
const handleThemeChange = (newTheme) => {
setTheme(newTheme);
localStorage.setItem("theme", newTheme);
// Storage Event триггеритсяся в других вкладках!
};
return <button onClick={() => handleThemeChange("dark")}>Switch</button>;
}
Способ 2: BroadcastChannel API (современный способ)
class TabSyncManager {
constructor(channelName = "app-sync") {
this.channel = new BroadcastChannel(channelName);
this.listeners = new Map();
}
subscribe(event, callback) {
if (!this.listeners.has(event)) {
this.listeners.set(event, []);
}
this.listeners.get(event).push(callback);
if (this.listeners.get(event).length === 1) {
this.channel.addEventListener("message", (msg) => {
if (msg.data.type === event) {
this.listeners.get(event).forEach(cb => cb(msg.data.payload));
}
});
}
}
publish(event, payload) {
this.channel.postMessage({
type: event,
payload
});
}
}
const syncManager = new TabSyncManager("app-sync");
function UserProfile() {
const [user, setUser] = useState(null);
useEffect(() => {
syncManager.subscribe("user-updated", (updatedUser) => {
setUser(updatedUser);
});
}, []);
const updateUserData = async (newData) => {
const response = await fetch(`/api/user`, {
method: "PUT",
body: JSON.stringify(newData)
});
const updatedUser = await response.json();
setUser(updatedUser);
syncManager.publish("user-updated", updatedUser);
};
return (
<div>
{user && (
<>
<h1>{user.name}</h1>
<button onClick={() => updateUserData({ name: "Alice" })}>
Update
</button>
</>
)}
</div>
);
}
Способ 3: Сочетание Storage Event + Server Updates
class UserSyncService {
constructor() {
this.callbacks = [];
}
subscribe(callback) {
this.callbacks.push(callback);
}
initialize() {
window.addEventListener("storage", (event) => {
if (event.key === "user-state" && event.newValue) {
const newUser = JSON.parse(event.newValue);
this.notifySubscribers(newUser);
}
});
}
async updateUser(userId, updates) {
try {
const response = await fetch(`/api/users/${userId}`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(updates)
});
const updatedUser = await response.json();
localStorage.setItem("user-state", JSON.stringify(updatedUser));
this.notifySubscribers(updatedUser);
return updatedUser;
} catch (error) {
console.error("Error updating user:", error);
}
}
notifySubscribers(user) {
this.callbacks.forEach(cb => cb(user));
}
}
const userSyncService = new UserSyncService();
userSyncService.initialize();
function UserProfile() {
const [user, setUser] = useState(null);
useEffect(() => {
userSyncService.subscribe(setUser);
const savedUser = localStorage.getItem("user-state");
if (savedUser) {
setUser(JSON.parse(savedUser));
}
}, []);
const handleUpdate = async (newData) => {
await userSyncService.updateUser(user.id, newData);
};
if (!user) return <div>Loading...</div>;
return (
<div>
<h1>{user.name}</h1>
<button onClick={() => handleUpdate({ name: "New Name" })}>
Update Profile
</button>
</div>
);
}
Способ 4: URL Syncing (для простых случаев)
function useUrlSync(key, initialValue) {
const [value, setValue] = useState(() => {
const params = new URLSearchParams(window.location.search);
return params.get(key) || initialValue;
});
useEffect(() => {
const handlePopState = () => {
const params = new URLSearchParams(window.location.search);
setValue(params.get(key) || initialValue);
};
window.addEventListener("popstate", handlePopState);
return () => {
window.removeEventListener("popstate", handlePopState);
};
}, [key, initialValue]);
const updateValue = (newValue) => {
setValue(newValue);
const params = new URLSearchParams(window.location.search);
params.set(key, newValue);
window.history.pushState(null, "", `?${params.toString()}`);
};
return [value, updateValue];
}
function FilteredList() {
const [sortBy, setSortBy] = useUrlSync("sort", "name");
return (
<div>
<select value={sortBy} onChange={(e) => setSortBy(e.target.value)}>
<option value="name">By Name</option>
<option value="date">By Date</option>
</select>
</div>
);
}
Сравнение методов
- localStorage + Storage Event: просто, поддержка всех браузеров, но медленно
- BroadcastChannel: современно, быстро, отличная поддержка
- SharedWorker: мощно, для сложных сценариев
- URL Syncing: просто, подходит для состояния в URL