Skip to content

Some non-hook function results are not preserved after conditional OutPortal #39

Open
@paulsohn

Description

@paulsohn

I would like to

  • transfer components which render results depend on nondeterministic, direct function call(such as Math.random() or Date.now()) and
  • fix the results before and after transfer, without storing them internally
  • OutPortal for the components are conditional

And for these purposes, it seems like this package doesn't do what I expected.


Here's the MWE example: CodeSandbox Link

//Box.tsx
function Box({ txt }: BoxProps) {
  return (
    <div style={{ border: "1px solid red" }}>
      <strong>{txt}</strong>
      <br />
      {Date.now()}
    </div>
  );
}
export const MemoBox = memo(Box);
//App.tsx
import { MemoBox } from "./Box";

function App() {
  const [show, setShow] = useState(true);
  const node1 = useMemo(createHtmlPortalNode, []);
  const node2 = useMemo(createHtmlPortalNode, []);
  const toggle = useCallback(() => { setShow((show) => !show); }, []);

  return (
    <>
      <InPortal node={node1}> <MemoBox txt="111" /> </InPortal>
      <InPortal node={node2}> <MemoBox txt="222" /> </InPortal>
      <button onClick={toggle}>Click me!</button>

      <div>
        {show && <OutPortal node={node1} />}
        {show && <OutPortal node={node2} />}
      </div>
      <div>
        {!show && <OutPortal node={node1} />}
      </div>
    </>
  );
}

Every time I toggle, Box "111" alters its position and Box "222" repeats hiding and showing. Now let's focus on the timestamp.
Box "111" prints with the fixed timestamp, because every render exposes either OutPortal node1. This is fine.
Box "222" however, the timestamp has changed on every show, and this behavior is not quite intuitive when the package description says:

Rendering to zero OutPortals is fine: the node will be rendered as normal, using just the props provided inside the InPortal definition, and will continue to happily exist but just not appear in the DOM anywhere.

rather, it looks like when OutPortal node2 is absent, the detached node2 has been garbage collected and previous timestamp is gone forever.

I'm aware that below solutions will fix Box "222" timestamp as well, so that my purposes are satisfied:

  • modifying the Box implementation (namely using useEffect and useState) to store the timestamp right after mounting
  • or even simpler, insert !show && OutPortal node2 as well but wrapped inside a display:none div.

yet I'm curious why did the timestamp change when the component is supposed to be memoized - given that without these portals React.memo() will do its work and fix the whole results.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions