My problem is, that I can not detect correct theme on a dialog after it’s switched.
Steps to reproduce:
- Add provided component to the panel - it will render a button
- Click the button - it will show a dialog with active theme
- Close dialog
- Switch theme
- Click button again - still shows old theme
I want it to show current theme
This is a very simplified version of what I have (kept it in a single file to not pollute the code)
// DialogThemeTest.tsx
import ReactDOM from "react-dom";
import photoshop from "photoshop";
const Theme = {
DARKEST: "darkest",
DARK: "dark",
LIGHT: "light",
LIGHTEST: "lightest",
}
const ThemeMap = new Map([
["kPanelBrightnessDarkGray", Theme.DARKEST],
["kPanelBrightnessMediumGray", Theme.DARK],
["kPanelBrightnessLightGray", Theme.LIGHT],
["kPanelBrightnessOriginal", Theme.LIGHTEST],
])
const getTheme = () => {
const result = photoshop.action.batchPlay(
[
{
"_obj": "get",
"_target": [
{"_property": "kuiBrightnessLevel"},
{"_ref": "application", "_enum": "ordinal", "_value": "targetEnum"}
],
"_options": {"dialogOptions": "dontDisplay"}
}
],
{"synchronousExecution": true});
const pinned = result[0].kuiBrightnessLevel._value;
return ThemeMap.get(pinned) ?? Theme.DARK
}
const DialogContent = ({dialog}) =>
<>
<sp-body>{getTheme()}</sp-body>
<sp-footer>
<sp-button onClick={() => dialog.close()}>Close</sp-button>
</sp-footer>
</>
const dialogController = (Component) => {
let dialogElement
return async () => {
if (!dialogElement) {
dialogElement = document.createElement("dialog");
ReactDOM.render(Component({dialog: dialogElement}), dialogElement);
}
document.body.appendChild(dialogElement);
await dialogElement.uxpShowModal({title: "dialogThemeTest"});
dialogElement.remove();
}
}
const dialogOpener = () => dialogController(DialogContent)
export default () =>
<sp-button onClick={dialogOpener()}>
Check theme
</sp-button>
I was thinking about unmounting the dialog, but it gives me error it was rendered with different version of React
Could you maybe post a screenshot or video demonstrating the behavior? I think I didn’t quite understand the full use case yet.
Also, you could try to put a console.log
into the getTheme
function. My first suspicion is that this gets only called once at the start.
And you’re right. It is called only once. I believe it’s because DialogContent
is already in a variable and you can pass it to dialog controller what you want - it already has the result of getTheme()
. So that’s the part I can’t figure out how to force re-render or reinitiate that content.
I also believe it should work with proper unmount by calling ReactDOM.unmountComponentAtNode(dialogElement)
right before dialogElement.remove()
, but then I get this error “The node you’re attempting to unmount was rendered by another copy of React” (scroll down to the very last comment)
Unmounting dialogElement
will not solve the issue, which is that the DialogContent
component does not re-render. There are multiple ways to do that, one of that is updating its props. Interestingly, even if you create a new dialog
element every time, this doesn’t trigger a re-render as React thinks it’s the same object. Passing the current theme however works fine:
const DialogContent = ({dialog, theme}) => {
return <>
<sp-body>{theme}</sp-body>
<sp-footer>
<sp-button onClick={() => dialog.close()}>Close</sp-button>
</sp-footer>
</>
}
const dialogController = (Component) => {
let dialogElement
return async () => {
dialogElement = document.createElement("dialog");
ReactDOM.render(Component({dialog: dialogElement, theme: getTheme()}), dialogElement);
document.body.appendChild(dialogElement);
await dialogElement.uxpShowModal({title: "dialogThemeTest"});
dialogElement.remove();
}
}
If you rather want to encapsulate the theme fetching logic in the DialogContent
as you did before, I’d probably make use of the key
property that React uses to reference component instances.
const DialogContent = ({dialog}) => {
return <>
<sp-body>{getTheme()}</sp-body>
<sp-footer>
<sp-button onClick={() => dialog.close()}>Close</sp-button>
</sp-footer>
</>
}
const dialogController = (Component) => {
let dialogElement
return async () => {
dialogElement = document.createElement("dialog");
ReactDOM.render(Component({dialog: dialogElement, key: Math.random()}), dialogElement);
document.body.appendChild(dialogElement);
await dialogElement.uxpShowModal({title: "dialogThemeTest"});
dialogElement.remove();
}
}
Instead of Math.random, you could also assign a unique UUID for the dialog every time. There’s probably a more elegant solution to solve all this, but this was the first thing that came to my mind.
Thans once again I’ll stick with the key
solution at least until I become aware of a better one, because I think it’s more correct - when I close the dialog, I want it to re-rendered next time I open it. This actually might also help solving another very similar issue I’m having (will check that later)
This is the bit I changed to (always creating new dialogElement
and uuid()
on dialog open)
return async () => {
const dialogElement = document.createElement("dialog")
ReactDOM.render(Component({key: uuid(), dialog: dialogElement}), dialogElement)
document.body.appendChild(dialogElement);
await dialogElement.uxpShowModal(dialogOpts)
dialogElement.remove()
}