Software development at Scale!
@complancoder
Engineering Manager @
Microsoft Teams
Microsoft Teams
1000+ Engineers
3 Continents
325,000 Organizations
8M+ Lines of Code
Microsoft Teams
2015
2016
2017
2018
Move to React for all UI code
Non UI services moves to framework agnostic code
Each Micro frontends doing it own release
Developer productivity
Faster Builds
How do we put React in Angular ?
How do we move non UI/services code to North star progressively ?
How do we not impact the bundle size ?
Awesome React!
Angular Directive
Awesome React!
Empty Directive
Awesome React!
Awesome React!
Angular App
Data
Events
React-Angular Bridge
var directive = reactBridgeDirective
.forLazyLoadedComponent(() =>
import("@msteams/component")
.then(_ => _.AwesomeReactComponent)
)
.withInjected("loggerService").ofType<ILoggerService>()
.withInjected("someAngularService").ofType<someAngularService>()
.setUpComponentProps((services) => {
return {
onButtonClick:() => { /* more code */},
randomProp: "Hello"
}
})
.onComponentDidMount((component, injectedServices) => {
/* Call functions on the component */
})
.create();
angular.module("app").directive('componentWrapper', directive);
<div>
<component-wrapper ng-if="ctrl.isReactEnabled"></component-wrapper>
</div>
reactBridgeDirective
.forLazyLoadedComponent(() =>
import("@msteams/component")
.then(_ => _.AwesomeReactComponent)
)
.withInjected("someAngularService").ofType<someAngularService>()
.setUpComponentProps((services) => {
return {
onButtonClick:() => { /* more code */},
randomProp: "Hello"
}
})
.onComponentDidMount((component, injectedServices) => {
component.printSomething(
injectedServices.someAngularService.getString()
);
})
.create();
class Wrapper extends React.Component {
/* other component related code*/
printSomething(str) {
console.log(str);
}
render() {
return (
<>
{{this.props.randomProp}}
<button onClick={this.props.onButtonClick}></button>
</>
);
}
}
reactBridgeDirective
.forLazyLoadedComponent(/* use Dynamic import*/ )
.withInjected("loggerService").ofType<ILoggerService>()
.withManualRendering((render) => {
return new Promise((resolve, reject) => {
requestAnimationFrame(() => {
/*Render React component whenever you want to*/
});
});
})
.setUpTelemetry((services) => {
/* return scenarios for telemetry*/
const scenarios = services.constants.getConstant("scenarios");
return [ scenarios.scenarioOne, scenarios.scenarioTwo ];
})
.withErrorHandling((error, element, injectedServices) => {
/* log the error or display a custom error component*/
})
.setUpComponentProps(() => (
/* Send props to React for 2 way communication*/
))
.onComponentDidMount((component, injectedServices) => {
/* Log or do something else*/
})
.onComponentWillUnmount((component, injectedServices) => {
/* Log or do something else*/
})
.create();
• Simple, Readable & Easy
• Less boilerplate and scaffolding
• React Code base unaware of Angular
Angular
Router
/calendar
/files
/calling
React Component
React Component
Angular Component
React Keeps memory of last initialized Root
React still keeps references of the DOM nodes even if unmountComponentAtNode is called.
Leaked memory pops up as an Array of Fiber Nodes
The library fixes this issue by rendering a Fragment on unmount.
import * as React from "react";
import { SharepointFilesContainer } from "./containers";
import { LoggingService } from "@msteams/services-logging";
class SharePointFilesLayout extends React.Component<{}> {
constructor(props: any) {
super(props);
}
public render() {
LoggingService.logInfo("Rendering Sharepoint Files View");
return <SharepointFilesContainer />;
}
}
export { SharePointFilesLayout };
?
resolve : {
alias : {
"@msteams/services-logging": "shims/loggingServiceShim.ts"
}
}
export const LoggingService: ILoggingService =
angular.element(document.body).injector().get('bridgeLoggingService');
loggingServiceShim.ts
import { map } from "lodash"
import { http } from "./http"
Lodash
resolve : {
alias : {
"lodash": "customLodash/index.ts"
}
}
Custom Lodash
Awesome Component
import { map } from "lodash"
import { http } from "./http"
Lodash
resolve : {
alias : {
"lodash": "customLodash/index.ts"
}
}
Custom Lodash
Awesome Component
Custom Lodash
- Abhik Mitra
@complancoder
linkedin.com/in/iamabhik