Skip to content

Commit 7261d33

Browse files
authored
Add sample for using different version of React in hosting app via NPM packages (#2509)
* Add sample 23.a.hybrid-react-npm * Prettier * Update PR number * Update limitations * Verbage * Add entry * Apply suggestions from code review Co-Authored-By: Corina <[email protected]> * Typo * Fix table formatting
1 parent 55dee85 commit 7261d33

31 files changed

Lines changed: 25686 additions & 0 deletions

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
118118
- [Single sign-on for Microsoft Teams apps](https://microsoft.github.io/BotFramework-WebChat/19.c.single-sign-on-for-teams-apps/), by [@compulim](https://github.com/compulim) in [#2196](https://github.com/microsoft/BotFramework-WebChat/pull/2196)
119119
- [Customize Web Chat with Reaction Buttons](https://microsoft.github.io/BotFramework-WebChat/09.customization-reaction-buttons/). Updated reaction handlers to send `messageReaction` activities, by [@tdurnford](https://github.com/tdurnford) in [#2239](https://github.com/microsoft/BotFramework-WebChat/pull/2239)
120120
- [Select voice for speech synthesis](https://microsoft.github.io/BotFramework-WebChat/06.g.select-voice/), by [@compulim](https://github.com/compulim), in PR [#2338](https://github.com/microsoft/BotFramework-WebChat/pull/2338)
121+
- [Using different versions of React on a hosting app via NPM packages](https://microsoft.github.io/BotFramework-WebChat/23.a.hybrid-react-npm/), by [@compulim](https://github.com/compulim), in PR [#2509](https://github.com/microsoft/BotFramework-WebChat/pull/2509)
121122

122123
## [4.5.3] - 2019-10-10
123124

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ There is a breaking change on behavior expectations regarding speech and input h
209209
| [`20.a.upload-to-azure-storage`](https://github.com/microsoft/BotFramework-WebChat/tree/master/samples/20.a.upload-to-azure-storage) | Demonstrates how to use upload attachments directly to Azure Storage | [Upload to Azure Storage Demo](https://microsoft.github.io/BotFramework-WebChat/20.a.upload-to-azure-storage) |
210210
| [`21.customization-plain-ui`](https://github.com/microsoft/BotFramework-WebChat/tree/master/samples/21.customization-plain-ui) | Advanced tutorial: Demonstrates how to customize the Web Chat UI by building from ground up instead of needing to rewrite entire Web Chat components. | [Plain UI Demo](https://microsoft.github.io/BotFramework-WebChat/21.customization-plain-ui) |
211211
| [`22.customization-change-locale`](https://github.com/microsoft/BotFramework-WebChat/tree/master/samples/22.customization-change-locale) | Demonstrates how to change locale when an activity is received from the bot | [Change Locale Demo](https://microsoft.github.io/BotFramework-WebChat/22.customization-change-locale) |
212+
| [`23.a.hybrid-react-npm`](https://github.com/microsoft/BotFramework-WebChat/tree/master/samples/23.a.hybrid-react-npm) | Demonstrates how to use different versions of React on a hosting app via NPM packages | [Hybrid React Demo](https://microsoft.github.io/BotFramework-WebChat/23.a.hybrid-react-npm) |
212213

213214
# Web Chat API Reference
214215

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/node_modules
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
# Using different versions of React for hosting an app with Web Chat
2+
3+
In Web Chat version 4.6, we migrated to React Hooks, which requires React 16.8.6 and up.
4+
5+
In this sample, we will show how to use Web Chat in a hosting app with React version 16.0.0, instead of the required 16.8.6.
6+
7+
There are several key limitations in this sample. They are outlined in [this section](#why-not-to-use-two-versions-of-react).
8+
9+
# How to run locally
10+
11+
![Web Chat using React 16.8.6 hosting in an app using React 16.0.0](docs/screenshot1.png)
12+
13+
To run this sample, follow steps below:
14+
15+
1. Clone this repository
16+
1. Run `npm install`
17+
1. Run `npm start`
18+
1. Browse to http://localhost:3000/
19+
20+
# How it works
21+
22+
There are two packages in this [monorepo](https://en.wikipedia.org/wiki/Monorepo):
23+
24+
- `app`: the hosting app, created using [`create-react-app`](https://github.com/facebook/create-react-app)
25+
26+
- `chat-component`: the chat component, which will render Web Chat
27+
28+
29+
The hosting app will create an [isolated DOM element](https://reactjs.org/docs/integrating-with-other-libraries.html) and pass it to the `chat-component` package. The chat component will control the rendering of the DOM element, while the hosting app controls the lifetime.
30+
31+
The hosting app will tell the chat component when it is time to remove the DOM element from the tree, and the chat component should stop any further rendering.
32+
33+
## Chat component
34+
35+
In `chat-component` package, we created an entrypoint for rendering a React component to a specific DOM element. Note that we are using `[email protected]` when mounting and unmounting the component to the DOM.
36+
37+
```jsx
38+
import { render, unmountComponentAtNode } from 'react-dom';
39+
40+
function renderChatComponent(props, node) {
41+
render(<ChatComponent {...props} />, node);
42+
43+
return () => unmountComponentAtNode(node);
44+
}
45+
```
46+
47+
> This entrypoint will return a function, which, when called, will unmount the component. This function call can be [called multiple times to update the props](https://reactjs.org/docs/react-dom.html#render).
48+
49+
## Hosting app
50+
51+
In the host application, we created a new component called `<ChatComponentWrapper>` and save the reference.
52+
53+
```jsx
54+
class ChatComponentWrapper extends React.Component {
55+
constructor(props) {
56+
super(props);
57+
58+
this.saveChatComponentRef = ref => this.chatComponentRef = ref;
59+
}
60+
61+
render() {
62+
return (
63+
<div ref={ this.saveChatComponentRef } />
64+
);
65+
}
66+
}
67+
```
68+
69+
When `<ChatComponentWrapper>` is mounted or updated, we will call `chat-component` to do the rendering.
70+
71+
```diff
72+
+ import renderChatComponent from 'chat-component';
73+
74+
class ChatComponentWrapper extends React.Component {
75+
constructor(props) {
76+
super(props);
77+
78+
this.saveChatComponentRef = ref => this.chatComponentRef = ref;
79+
}
80+
81+
+ componentDidMount() {
82+
+ this.componentDidMountOrUpdate();
83+
+ }
84+
85+
+ componentDidUpdate() {
86+
+ this.componentDidMountOrUpdate();
87+
+ }
88+
89+
+ componentDidMountOrUpdate() {
90+
+ renderChatComponent(this.props, this.chatComponentRef);
91+
+ }
92+
93+
render() {
94+
return (
95+
<div ref={ this.saveChatComponentRef } />
96+
);
97+
}
98+
}
99+
```
100+
101+
When it is time to unmount, we will call to the `chat-component` to unmount the component.
102+
103+
```diff
104+
import renderChatComponent from 'chat-component';
105+
106+
class ChatComponentWrapper extends React.Component {
107+
constructor(props) {
108+
super(props);
109+
110+
this.saveChatComponentRef = ref => this.chatComponentRef = ref;
111+
}
112+
113+
componentDidMount() {
114+
this.componentDidMountOrUpdate();
115+
}
116+
117+
componentDidUpdate() {
118+
this.componentDidMountOrUpdate();
119+
}
120+
121+
componentDidMountOrUpdate() {
122+
- renderChatComponent(this.props, this.chatComponentRef);
123+
+ this.unmountChatComponent = renderChatComponent(this.props, this.chatComponentRef);
124+
}
125+
126+
+ componentWillUnmount() {
127+
+ this.unmountChatComponent();
128+
+ }
129+
130+
render() {
131+
return (
132+
<div ref={ this.saveChatComponentRef } />
133+
);
134+
}
135+
}
136+
```
137+
138+
# Why is this sample using two versions of React
139+
140+
Although we recommend that you upgrade your host app at your earliest convenience, we understand that host app may need some time before its React dependencies are updated, especially in regards to huge applications.
141+
142+
As stated in [this article](https://reactjs.org/warnings/invalid-hook-call-warning.html), React requires that all components using hooks exist in the same React DOM tree, and must use the _same instance_ of React.
143+
144+
In this approach, we isolated the DOM element from the React DOM tree. Then we use another `react-dom` package to continue the rendering. So the React DOM tree is virtually divided into two parts, although they looks contiguous. This approach is a [supported scenario outlined in React docs](https://reactjs.org/docs/integrating-with-other-libraries.html).
145+
146+
# Why not to use two versions of React
147+
148+
There are several key limitations to this approach:
149+
150+
- Increased memory usage and bundle size
151+
- Only `props` can be passed between two DOM trees; React Context cannot be passed between them without extra work
152+
- It is doable, but will require two different context objects and they must be wired up manually
153+
- This includes `<Provider>` and `connect()` HOC in `react-redux`
154+
- [React-based customizations](https://github.com/microsoft/botframework-webchat#customize-web-chat-ui) added to Web Chat will still require React 16.8.6 or up
155+
- For example, activity and attachment middleware require the newer version of React
156+
- Introducing new package(s), which could increase unnecessary fragmentation in your codebase
157+
- The rendering processes between two DOM trees are asynchronous and not guaranteed to complete at the same time
158+
159+
# Further reading
160+
161+
- [Web Chat: Customize Web Chat UI](https://github.com/microsoft/botframework-webchat#customize-web-chat-ui)
162+
- [React: Rules of Hooks](https://reactjs.org/docs/hooks-rules.html)
163+
- [React: Integrating with Other Libraries](https://reactjs.org/docs/integrating-with-other-libraries.html)
164+
- [React: Invalid Hook Call Warning](https://reactjs.org/warnings/invalid-hook-call-warning.html)
9.13 KB
Loading
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"packages": [
3+
"packages/*"
4+
],
5+
"version": "0.0.0"
6+
}

0 commit comments

Comments
 (0)