From e17a39c66036279e5ef53b33f1a95fb555adb094 Mon Sep 17 00:00:00 2001 From: Sebastian Markbage Date: Mon, 10 Dec 2018 13:38:05 -0800 Subject: [PATCH 1/2] Custom Host Node RFC --- text/0000-custom-host-node.md | 62 +++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 text/0000-custom-host-node.md diff --git a/text/0000-custom-host-node.md b/text/0000-custom-host-node.md new file mode 100644 index 00000000..c7824850 --- /dev/null +++ b/text/0000-custom-host-node.md @@ -0,0 +1,62 @@ +- Start Date: 2018-12-10 +- RFC PR: (leave this empty) +- React Issue: (leave this empty) + +# Custom Host Node + +I was thinking about the use case for useMutationEffect. The use case is to implement your own manual updates to a DOM node, or to a DOM node via a legacy imperative API like jQuery. + +The idea is that it is better to do before useLayoutEffect and componentDidMount because they might read layout + followed by setState. So if the first sibling reads layout, the second mutates the DOM, the third reads layout, you get two layout passes instead of one. + +However, the problem with useMutationEffect is that refs haven't been swapped yet. During initial render, you don't have access to the new element that you want to mutate. During updates, you have access to the old element but not the new one if a ref has swapped instance. + +Another issue with the useMutationEffect API is that normally React does something special during a newly added tree. Instead of performing mutations in the commit phase, we do all the initialization and mutation during the render phase. Only updates get applied. + +Another quirk is that these can never really be renderer agnostic since the refs resolve to different things depending on where you render them. Additionally, some environments don't even have mutation APIs like React Fabric. Instead it has a clone API. + +So maybe the API should be renderer specific and designed around this exact use case. + +## Alternative #1: Hook API + +```js +import {useDOMNode} from "react-dom"; + +function Component(props) { + let manualChildElement = useDOMNode(() => { + // create + let node = document.createElement('div'); + node.style.color = props.color; + return node; + }, node => { + // update + node.style.color = props.color; + }, node => { + // clean up + }); + + return
{manualChildElement}
; +} +``` + +## Alternative #2: HoC API + +```js +import {createManualDOMNode} from "react-dom"; +let CustomDOMNode = createManualDOMNode(props => { + // create + let node = document.createElement('div'); + node.style.color = props.color; + return node; +}, (props, node) => { + // update + node.style.color = props.color; +}, (props, node) => { + // clean up +}); + +function Component(props) { + return
; +} +``` + + From 82de40168ffad4e03c0c037cb103f029658959ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= Date: Mon, 10 Dec 2018 13:58:03 -0800 Subject: [PATCH 2/2] Update 0000-custom-host-node.md --- text/0000-custom-host-node.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-custom-host-node.md b/text/0000-custom-host-node.md index c7824850..66d4bdb1 100644 --- a/text/0000-custom-host-node.md +++ b/text/0000-custom-host-node.md @@ -10,7 +10,7 @@ The idea is that it is better to do before useLayoutEffect and componentDidMount However, the problem with useMutationEffect is that refs haven't been swapped yet. During initial render, you don't have access to the new element that you want to mutate. During updates, you have access to the old element but not the new one if a ref has swapped instance. -Another issue with the useMutationEffect API is that normally React does something special during a newly added tree. Instead of performing mutations in the commit phase, we do all the initialization and mutation during the render phase. Only updates get applied. +Another issue with the useMutationEffect API is that normally React does something special during a newly added tree. Instead of performing mutations in the commit phase, we do all the initialization and mutation during the render phase. Only updates get applied in the commit phase. That special case gets tricky to manage with refs and effects. Another quirk is that these can never really be renderer agnostic since the refs resolve to different things depending on where you render them. Additionally, some environments don't even have mutation APIs like React Fabric. Instead it has a clone API.