I would like to open and close an antd Modal component using a MobX store. I have the following code Here's a link to codesandbox https://codesandbox.io/s/nifty-dijkstra-g0kzs6?file=/src/App.js
import AppNavigation from "./components/Menu";
import ContactPopUp from "./components/Contact";
export default function App() {
return (
<div className="App">
<AppNavigation />
<ContactPopUp />
</div>
);
}
File with the MobXstore
import { createContext, useContext } from "react";
import AppStore from "./appStore";
interface Store {
appStore: AppStore;
}
export const store: Store = {
appStore: new AppStore()
};
export const StoreContext = createContext(store);
export function useStore() {
return useContext(StoreContext);
}
Separate file where I declare the store
import { makeAutoObservable } from "mobx";
export default class AppStore {
contactFormOpen = false;
constructor() {
makeAutoObservable(this);
}
setContactFormOpen = (isOpen: boolean) => {
console.log("Changed contact form to ", isOpen);
this.contactFormOpen = isOpen;
};
}
The Menu.tsx
import React from "react";
import { Menu, MenuProps } from "antd";
import { useStore } from "../store/store";
const AppNavigation = () => {
const { appStore } = useStore();
const menuItems: MenuProps["items"] = [
{
label: <a onClick={(e) => handleOpenContactForm(e)}>Contact</a>,
key: "Contact"
}
];
const handleOpenContactForm = (e: any) => {
e.preventDefault();
e.stopPropagation();
appStore.setContactFormOpen(true);
console.log("Open contact pop up", appStore.contactFormOpen);
};
return (
<Menu
items={menuItems}
theme="dark"
overflowedIndicator={""}
className="header__menu award-menu header__menu--md"
/>
);
};
export default AppNavigation;
ContactPopUp.tsx
import { Modal } from "antd";
import React, { useEffect, useState } from "react";
import { useStore } from "../store/store";
const ContactPopUp = () => {
const { appStore } = useStore();
const [visible, setVisible] = useState(appStore.contactFormOpen);
useEffect(() => {
setVisible(appStore.contactFormOpen);
}, [appStore.contactFormOpen]);
const handleCancel = () => {
appStore.setContactFormOpen(false);
console.log("Close contact from", appStore.contactFormOpen);
};
return (
<Modal title="Contact us" visible={visible} onCancel={handleCancel}>
<h2>Modal Open</h2>
</Modal>
);
};
export default ContactPopUp;
The mobx contactFormOpen clearly changes but the modal state does not. I really don't understand why... UseEffect also doesn't trigger a re render.
CodePudding user response:
You just forgot most crucial part - every component that uses any observable value needs to be wrapped with observer
decorator! Like that:
const ContactPopUp = () => {
const { appStore } = useStore();
const handleCancel = () => {
appStore.setContactFormOpen(false);
console.log('Close contact from', appStore.contactFormOpen);
};
return (
<Modal
title="Contact us"
visible={appStore.contactFormOpen}
onCancel={handleCancel}
>
<h2>Modal Open</h2>
</Modal>
);
};
// Here I've added `observer` decorator/HOC
export default observer(ContactPopUp);
And you don't need useEffect
or anything like that now.