# react-reconciler
This is an experimental package for creating custom React renderers.
**Its API is not as stable as that of React, React Native, or React DOM, and does not follow the common versioning scheme.**
**Use it at your own risk.**
## Usage
```js
const Reconciler = require('react-reconciler');
const HostConfig = {
// You'll need to implement some methods here.
// See below for more information and examples.
};
const MyRenderer = Reconciler(HostConfig);
const RendererPublicAPI = {
render(element, container, callback) {
// Call MyRenderer.updateContainer() to schedule changes on the roots.
// See ReactDOM, React Native, or React ART for practical examples.
}
};
module.exports = RendererPublicAPI;
```
## Practical Examples
A "host config" is an object that you need to provide, and that describes how to make something happen in the "host" environment (e.g. DOM, canvas, console, or whatever your rendering target is). It looks like this:
```js
const HostConfig = {
createInstance(type, props) {
// e.g. DOM renderer returns a DOM node
},
// ...
supportsMutation: true, // it works by mutating nodes
appendChild(parent, child) {
// e.g. DOM renderer would call .appendChild() here
},
// ...
};
```
**For an introduction to writing a very simple custom renderer, check out this article series:**
* **[Building a simple custom renderer to DOM](https://medium.com/@agent_hunt/hello-world-custom-react-renderer-9a95b7cd04bc)**
* **[Building a simple custom renderer to native](https://medium.com/@agent_hunt/introduction-to-react-native-renderers-aka-react-native-is-the-java-and-react-native-renderers-are-828a0022f433)**
The full list of supported methods [can be found here](https://github.com/facebook/react/blob/main/packages/react-reconciler/src/forks/ReactFiberHostConfig.custom.js). For their signatures, we recommend looking at specific examples below.
The React repository includes several renderers. Each of them has its own host config.
The examples in the React repository are declared a bit differently than a third-party renderer would be. In particular, the `HostConfig` object mentioned above is never explicitly declared, and instead is a *module* in our code. However, its exports correspond directly to properties on a `HostConfig` object you'd need to declare in your code:
* [React ART](https://github.com/facebook/react/blob/main/packages/react-art/src/ReactART.js) and its [host config](https://github.com/facebook/react/blob/main/packages/react-art/src/ReactARTHostConfig.js)
* [React DOM](https://github.com/facebook/react/blob/main/packages/react-dom/src/client/ReactDOM.js) and its [host config](https://github.com/facebook/react/blob/main/packages/react-dom/src/client/ReactDOMHostConfig.js)
* [React Native](https://github.com/facebook/react/blob/main/packages/react-native-renderer/src/ReactNativeRenderer.js) and its [host config](https://github.com/facebook/react/blob/main/packages/react-native-renderer/src/ReactNativeHostConfig.js)
If these links break please file an issue and we’ll fix them. They intentionally link to the latest versions since the API is still evolving. If you have more questions please file an issue and we’ll try to help!
## An (Incomplete!) Reference
At the moment, we can't commit to documenting every API detail because the host config still changes very often between the releases. The documentation below is **provided in the spirit of making our best effort rather than an API guarantee**. It focuses on the parts that don't change too often. This is a compromise that strikes a balance between the need for a fast-paced development of React itself, and the usefulness of this package to the custom renderer community. If you notice parts that are out of date or don't match how the latest stable update is behaving, please file an issue or send a pull request, although a response might take time.
#### Modes
The reconciler has two modes: mutation mode and persistent mode. You must specify one of them.
If your target platform is similar to the DOM and has methods similar to `appendChild`, `removeChild`, and so on, you'll want to use the **mutation mode**. This is the same mode used by React DOM, React ART, and the classic React Native renderer.
```js
const HostConfig = {
// ...
supportsMutation: true,
// ...
}
```
If your target platform has immutable trees, you'll want the **persistent mode** instead. In that mode, existing nodes are never mutated, and instead every change clones the parent tree and then replaces the whole parent tree at the root. This is the node used by the new React Native renderer, codenamed "Fabric".
```js
const HostConfig = {
// ...
supportsPersistence: true,
// ...
}
```
Depending on the mode, the reconciler will call different methods on your host config.
If you're not sure which one you want, you likely need the mutation mode.
#### Core Methods
#### `createInstance(type, props, rootContainer, hostContext, internalHandle)`
This method should return a newly created node. For example, the DOM renderer would call `document.createElement(type)` here and then set the properties from `props`.
You can use `rootContainer` to access the root container associated with that tree. For example, in the DOM renderer, this is useful to get the correct `document` reference that the root belongs to.
The `hostContext` parameter lets you keep track of some information about your current place in the tree. To learn more about it, see `getChildHostContext` below.
The `internalHandle` data structure is meant to be opaque. If you bend the rules and rely on its internal fields, be aware that it may change significantly between versions. You're taking on additional maintenance risk by reading from it, and giving up all guarantees if you write something to it.
This method happens **in the render phase**. It can (and usually should) mutate the node it has just created before returning it, but it must not modify any other nodes. It must not register any event handlers on the parent tree. This is because an instance being created doesn't guarantee it would be placed in the tree — it could be left unused and later collected by GC. If you need to do something when an instance is definitely in the tree, look at `commitMount` instead.
#### `createTextInstance(text, rootContainer, hostContext, internalHandle)`
Same as `createInstance`, but for text nodes. If your renderer doesn't support text nodes, you can throw here.
#### `appendInitialChild(parentInstance, child)`
This method should mutate the `parentInstance` and add the child to its list of children. For example, in the DOM this would translate to a `parentInstance.appendChild(child)` call.
This method happens **in the render phase**. It can mutate `parentInstance` and `child`, but it must not modify any other nodes. It's called while the tree is still being built up and not connected to the actual tree on the screen.
#### `finalizeInitialChildren(instance, type, props, rootContainer, hostContext)`
In this method, you can perform some final mutations on the `instance`. Unlike with `createInstance`, by the time `finalizeInitialChildren` is called, all the initial children have already been added to the `instance`, but the instance itself has not yet been connected to the tree on the screen.
This method happens **in the render phase**. It can mutate `instance`, but it must not modify any other nodes. It's called while the tree is still being built up and not connected to the actual tree on the screen.
There is a second purpose to this method. It lets you specify whether there is some work that needs to happen when the node is connected to the tree on the screen. If you return `true`, the instance will receive a `commitMount` call later. See its documentation below.
If you don't want to do anything here, you should return `false`.
#### `prepareUpdate(instance, type, oldProps, newProps, rootContaine