So, I'm in the process of doing a big update to an office add-in, but somewhere along the line I've wound up with a strange bug. While my add-in renders with the data I'd expect, no event callbacks (such as onClick listeners) seem to, well, react when I interact with it. What's odd is that this behavior would seem to originate early in my app, before I've deviated too far from the template. I'm wondering if this might be some sort of versioning issue, for reasons I'll get into below, but to bring in the highlights of my code:
For my entrypoint, index.tsx:
initializeIcons();
let isOfficeInitialized = false;
const title = "Placeholder";
const render = (Component) => {
ReactDOM.render(
<AppContainer>
<Component
title={title}
isOfficeInitialized={isOfficeInitialized}
/>
</AppContainer>,
document.getElementById("container")
);
};
/* Render application after Office initializes */
Office.onReady(() => {
isOfficeInitialized = true;
render(App);
});
if ((module as any).hot) {
(module as any).hot.accept("./components/App", () => {
const NextApp = require("./components/App").default;
render(NextApp);
});
}
And in app.tsx:
export default class App extends React.Component<AppProps, AppState> {
loginClick = async () => {
console.log("LoginClick fired");
try {
beginOAuth();
} catch (error) {
console.error(error);
}
};
getConditionalComponents() {
if (loggedIn) {
// Returns the actual app here.
} else {
// The DefaultButton here comes from FluentUI, but this does not appear to be
// limited to FluentUI controls.
return (
<div className="loginContainer">
<DefaultButton
className="ms-welcome__action"
buttonType={ButtonType.hero}
iconProps={{ iconName: "ChevronRight" }}
onClick={this.loginClick}
text="Sign in"
/>
</div>
);
}
}
render() {
const { title, isOfficeInitialized } = this.props;
if (!isOfficeInitialized) {
return (
<Progress title={title} logo="assets/logo-filled.png" message="Please sideload your addin to see app body." />
);
}
return (
<div className="ms-welcome">
<Header logo="assets/logo-notext.png" title={this.props.title} message="Welcome!" />
{this.getConditionalComponents()}
</div>
);
}
}
Now, everything renders as expected, but nothing happens when I click the login button. I can see the click event firing, and the button reacts visually as though it's been clicked, but eventually I can see react calling a noop function and my function is never called. The same behavior persists if I force myself to appear logged in, the app renders as I'd expect, but changing dropdowns, clicking buttons, etc all don't appear to do anything.
Now, one interesting thing is that if I change the Office.onReady
block in index.tsx back to what it used to be:
Office.initialize = () => {
isOfficeInitialized = True;
render(App);
};
my app will not render at first. However, if I right-click and refresh the taskpane hosting the app, it renders and behaves as expected.
This makes me wonder if this is somehow some sort of dependency mismatch issue. I'll try to cut my dependencies down to things that might be relevant:
"@hot-loader/react-dom": "^17.0.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"webpack": "^5.50.0",
"@babel/core": "^7.13.10",
I can provide any others if needed. Thanks in advance for any advice!
CodePudding user response:
Finally figured this out!
The issue stemmed from the fact that I'm using a shared javascript runtime, have some add-in commands hooked up to ribbon buttons, and built those commands in a separate chunk. The code for these add-in commands imported code that was also used by the taskpane. I'm not quite sure why this caused the behavior it did, since it shouldn't have been importing running anything from my index.tsx
(or really anything outside of some utility functions) but temporarily removing these imports got rid of this behavior, and as a workaround, I've moved the functions for my add-in commands into my index.tsx file, and set webpack to stop building the chunk they used to be in. It's a little ugly to have those commands in index.tsx
, but it works!