From 961fe7310fef2f3b41e2f602d2755b0dbafff3dd Mon Sep 17 00:00:00 2001 From: wacii Date: Wed, 26 Apr 2017 12:24:22 -0400 Subject: [PATCH] Add guide on integrating with non-react code (#9316) * Add guide on integrating with non-react code * Capitalize guide title * Make links to other docs relative * Rephrase 'What it does do' * Remove experimental syntax * Capitalize Backbone * Remove empty lifecycle method in generic jQuery example * Use shouldComponentUpdate() not componentWillUpdate() * Prefer single quotes * Add cleanup to generic jQuery example * Capitalize React * Generalize the section on Backbone Views * Generalize the section on Backbone Models, a little * Add introduction * Adjust wording * Simplify ref callbacks * Fix typo in generic jQuery example * Fix typos in Backbone models in React components * Fix more typos in Backbone models in React components * Add generic section on integrating with other view libraries * Stress the benefits of an unchanging React element * Small changes to introduction * Add missing semicolon * Revise generic jQuery wrapper section Moved the section on using empty elements to prevent conflicts above the code example and added brief introduction to that example. * Add usage example for Chosen wrapper * Prevent Chosen wrapper from updating * Note that sharing the DOM with plugins is not recommended * Mention how React is used at Facebook * Mention React event system in template rendering section * Remove destructuring from function parameters * Do not name React components Component * Elaborate on unmountComponentAtNode() * Mention preference for unidirectional data flow * Rename backboneModelAdapter * Replace rest syntax * Respond to updated model in connectToBackboneModel * Rewrite connectToBackboneModel example * Rework connectToBackboneModel example * Misc changes * Misc changes * Change wording * Tweak some parts (cherry picked from commit 1816d06d6ba778435ef39bc7988f3fb2e0569e80) --- docs/_data/nav_docs.yml | 2 + docs/docs/integrating-with-other-libraries.md | 439 ++++++++++++++++++ 2 files changed, 441 insertions(+) create mode 100644 docs/docs/integrating-with-other-libraries.md diff --git a/docs/_data/nav_docs.yml b/docs/_data/nav_docs.yml index 0401c7174b..7b562e2891 100644 --- a/docs/_data/nav_docs.yml +++ b/docs/_data/nav_docs.yml @@ -50,6 +50,8 @@ title: Web Components - id: higher-order-components title: Higher-Order Components + - id: integrating-with-other-libraries + title: Integrating with Other Libraries - title: Reference items: - id: react-api diff --git a/docs/docs/integrating-with-other-libraries.md b/docs/docs/integrating-with-other-libraries.md new file mode 100644 index 0000000000..4f393a9710 --- /dev/null +++ b/docs/docs/integrating-with-other-libraries.md @@ -0,0 +1,439 @@ +--- +id: integrating-with-other-libraries +title: Integrating with Other Libraries +permalink: docs/integrating-with-other-libraries.html +--- + +React can be used in any web application. It can be embedded in other applications and, with a little care, other applications can be embedded in React. This guide will examine some of the more common use cases, focusing on integration with [jQuery](https://jquery.com/) and [Backbone](http://backbonejs.org/), but the same ideas can be applied to integrating components with any existing code. + +## Integrating with DOM Manipulation Plugins + +React is unaware of changes made to the DOM outside of React. It determines updates based on its own internal representation, and if the same DOM nodes are manipulated by another library, React gets confused and has no way to recover. + +This does not mean it is impossible or even necessarily difficult to combine React with other ways of affecting the DOM, you just have to be mindful of what each are doing. + +The easiest way to avoid conflicts is to prevent the React component from updating. You can do this by rendering elements that React has no reason to update, like an empty `
`. + +### How to Approach the Problem + +To demonstrate this, let's sketch out a wrapper for a generic jQuery plugin. + +We will attach a [ref](/react/docs/refs-and-the-dom.html) to the root DOM element. Inside `componentDidMount`, we will get a reference to it so we can pass it to the jQuery plugin. + +To prevent React from touching the DOM after mounting, we will return an empty `
` from the `render()` method. The `
` element has no properties or children, so React has no reason to update it, leaving the jQuery plugin free to manage that part of the DOM: + +```js{3,4,8,12} +class SomePlugin extends React.Component { + componentDidMount() { + this.$el = $(this.el); + this.$el.somePlugin(); + } + + componentWillUnmount() { + this.$el.somePlugin('destroy'); + } + + render() { + return
this.el = el} />; + } +} +``` + +Note that we defined both `componentDidMount` and `componentWillUnmount` [lifecycle hooks](/react/docs/react-component.html#the-component-lifecycle). Many jQuery plugins attach event listeners to the DOM so it's important to detach them in `componentWillUnmount`. If the plugin does not provide a method for cleanup, you will probably have to provide your own, remembering to remove any event listeners the plugin registered to prevent memory leaks. + +### Integrating with jQuery Chosen Plugin + +For a more concrete example of these concepts, let's write a minimal wrapper for the plugin [Chosen](https://harvesthq.github.io/chosen/), which augments `` DOM node, it reads the attributes off of the original DOM node, hides it with an inline style, and then appends a separate DOM node with its own visual representation right after the `` wrapped in a `
`: + +```js{4,5} +class Chosen extends React.Component { + render() { + return ( +
+ +
+ ); + } +} +``` + +Notice how we wrapped `` node we passed to it. However, as far as React is concerned, `
` always only has a single child. This is how we ensure that React updates won't conflict with the extra DOM node appended by Chosen. It is important that if you modify the DOM outside of React flow, you must ensure React doesn't have a reason to touch those DOM nodes. + +Next, we will implement the lifecycle hooks. We need to initialize Chosen with the ref to the ` this.el = el}> +``` + +This is enough to get our component to render, but we also want to be notified about the value changes. To do this, we will subscribe to the jQuery `change` event on the ``, but we will also add a `componentDidUpdate()` lifecycle hook that notifies Chosen about changes in the children list: + +```js{2,3} +componentDidUpdate(prevProps) { + if (prevProps.children !== this.props.children) { + this.$el.trigger("chosen:updated"); + } +} +``` + +This way, Chosen will know to update its DOM element when the ` this.el = el}> + {this.props.children} + +
+ ); + } +} +``` + +[Try it on CodePen.](http://codepen.io/gaearon/pen/xdgKOz?editors=0010) + +## Integrating with Other View Libraries + +React can be embedded into other applications thanks to the flexibility of [`ReactDOM.render()`](/react/docs/react-dom.html#render). + +Although React is commonly used at startup to load a single root React component into the DOM, `ReactDOM.render()` can also be called multiple times for independent parts of the UI which can be as small as a button, or as large as an app. + +In fact, this is exactly how React is used at Facebook. This lets us write applications in React piece by piece, and combine it with our existing server-generated templates and other client-side code. + +### Replacing String-Based Rendering with React + +A common pattern in older web applications is to describe chunks of the DOM as a string and insert it into the DOM like so: `$el.html(htmlString)`. These points in a codebase are perfect for introducing React. Just rewrite the string based rendering as a React component. + +So the following jQuery implementation... + +```js +$('#container').html(''); +$('#btn').click(function() { + alert('Hello!'); +}); +``` + +...could be rewritten using a React component: + +```js +function Button() { + return ; +} + +ReactDOM.render( + ; +} + +function HelloButton() { + function handleClick() { + alert('Hello!'); + } + return