reactjs/react-rails

View on GitHub
docs/ujs.md

Summary

Maintainability
Test Coverage
# UJS

<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->

- [Mounting & Unmounting](#mounting--unmounting)
- [Event Handling](#event-handling)
- [`getConstructor`](#getconstructor)

<!-- END doctoc generated TOC please keep comment here to allow auto update -->


`react-rails`'s JavaScript is available as `"react_ujs"` in the asset pipeline or from NPM. It attaches itself to the window as `ReactRailsUJS`.

## Mounting & Unmounting

Usually, `react-rails` mounts & unmounts components automatically as described in [Event Handling](#event-handling) below.

You can also mount & unmount components from `<%= react_component(...) %>` tags using UJS:

```js
// Mount all components on the page:
ReactRailsUJS.mountComponents()
// Mount components within a selector:
ReactRailsUJS.mountComponents(".my-class")
// Mount components within a specific node:
ReactRailsUJS.mountComponents(specificDOMnode)

// Unmounting works the same way:
ReactRailsUJS.unmountComponents()
ReactRailsUJS.unmountComponents(".my-class")
ReactRailsUJS.unmountComponents(specificDOMnode)
```

You can use this when the DOM is modified by AJAX calls or modal windows.

## Event Handling

`ReactRailsUJS` checks for various libraries to support their page change events:

- `Turbolinks`
- `pjax`
- `jQuery`
- Native DOM events

`ReactRailsUJS` will automatically mount components on `<%= react_component(...) %>` tags and unmount them when appropriate.

If you need to re-detect events, you can call `detectEvents`:

```js
// Remove previous event handlers and add new ones:
ReactRailsUJS.detectEvents()
```

For example, if `Turbolinks` is loaded _after_ `ReactRailsUJS`, you'll need to call this again. This function removes previous handlers before adding new ones, so it's safe to call as often as needed.

If `Turbolinks` is `import`ed via Shakapacker (and thus not available globally), `ReactRailsUJS` will be unable to locate it. To fix this, you can temporarily add it to the global namespace:

```js
// Order is particular. First start Turbolinks:
Turbolinks.start();
// Add Turbolinks to the global namespace:
window.Turbolinks = Turbolinks;
// Remove previous event handlers and add new ones:
ReactRailsUJS.detectEvents();
// (Optional) Clean up global namespace:
delete window.Turbolinks;
```

## `getConstructor`

Components are loaded with `ReactRailsUJS.getConstructor(className)`. This function has two default implementations, depending on if you're using the asset pipeline or Shakapacker:

- On the asset pipeline, it looks up `className` in the global namespace (`ReactUJS.constructorFromGlobal`).
- On Shakapacker, it `require`s files and accesses named exports, as described in [Use with Shakapacker](./get-started.md#use-with-shakapacker), falling back to the global namespace (`ReactUJS.constructorFromRequireContextWithGlobalFallback`).

You can override this function to customize the mapping of name-to-constructor. [Server-side rendering](./server-side-rendering.md) also uses this function.

For example, the fallback behavior of
`ReactUJS.constructorFromRequireContextWithGlobalFallback` can sometimes make
server-side rendering errors hard to debug as it will swallow the original error
(more info
[here](https://github.com/reactjs/react-rails/issues/264#issuecomment-552326663)).
`ReactUJS.constructorFromRequireContext` is provided for this reason. You can
use it like so:

```js
// Replaces calls to `ReactUJS.useContext`
ReactUJS.getConstructor = ReactUJS.constructorFromRequireContext(require.context('components', true));
```