react: SSR: Cannot set property 'memoizedState' of null

Do you want to request a feature or report a bug?

A bug?

What is the current behavior?

Cannot set property ‘memoizedState’ of null

If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem. Your bug will get fixed much faster if we can run your code and it doesn’t have dependencies other than React. Paste the link to your JSFiddle (https://jsfiddle.net/Luktwrdm/) or CodeSandbox (https://codesandbox.io/s/new) example below:

const processLink = html => {
  return renderToStaticMarkup(<Link />)
};

const RichText = ({ html }) => {
  const htmlProcessed = useMemo(() => processLink(html), [html]);
}

See https://codesandbox.io/s/cannot-set-property-memoizedstate-of-null-mrxfr

What is the expected behavior?

Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?

16.8~16.9

About this issue

  • Original URL
  • State: open
  • Created 5 years ago
  • Reactions: 16
  • Comments: 27 (2 by maintainers)

Most upvoted comments

I’m having this issue as well using Next.js.

My experience aligns with @iamandrewluca - when using the ReactDOMServer.renderToString() method within a useMemo, I get the error: TypeError: Cannot set property 'memoizedState' of null

My notes on this issue.

This happens only when trying to renderToStaticMarkup(<App />); and inside App there is a useState, useReducer or useMemo that also uses renderToStaticMarkup

e.g.

import React, { useMemo, useReducer, useState } from "react";
import { renderToStaticMarkup } from "react-dom/server";

const hello = <h1>Hello Andrew!</h1>;
const reducer = (state: any) => state;

function App() {
  // Uncomment one of below lines and you will get a error
  // useMemo(() => renderToStaticMarkup(hello), []);
  // useState(() => renderToStaticMarkup(hello));
  // useReducer(reducer, "", () => renderToStaticMarkup(hello));
  return <div />;
}

renderToStaticMarkup(<App />);
TypeError: Cannot set property 'memoizedState' of null

ps: renderToStaticMarkup can be replaced with renderToString, will be same issue ps2: and if using useRef(renderToStaticMarkup(hello)); another error is showed Invalid hook call. Hooks can only be called...

https://codesandbox.io/s/nervous-liskov-ztt4r?file=/src/index.tsx

Mind if I take this?

@gaearon I tried to write a test but it seems it not fails in that environment. Did I miss something 🤔

it('should not crash when used inside useMemo', () => {
  function App() {
    React.useMemo(() => ReactDOMServer.renderToString(<span />), []);
    return <span />;
  }

  expect(() => ReactDOMServer.renderToString(<App />))
    .toThrowError(`Cannot set properties of null (setting 'memoizedState')`)
});
  ● ReactDOMServer › renderToString › should not crash when used inside useMemo

    expect(received).toThrowError(expected)

    Expected substring: "Cannot set properties of null (setting 'memoizedState')"

    Received function did not throw

This piece of code fails in cra browser, cra test, codesandbox and node (using createElement) with same error:

TypeError: Cannot set property 'memoizedState' of null

https://codesandbox.io/s/nervous-liskov-ztt4r?file=/src/index.tsx

image

@Lizzyrho21

There’s been a few attempts to fix it but so far they’ve tried to address the symptoms rather than the root cause. I think it might be easier for me to look into this, but what would be helpful is a PR with a failing test for this issue. Would you like to create one? ReactServerRendering-test.js might be a good place to add it. Run yarn test --watch ReactServerRendering-test and try to make a test that causes this error.

I can take a crack at this issue if nobody else has. Thanks!

Any updates on this ticket?

A reason

  • When renderToString(), renderToStaticMarkup() is triggered, new ReactPartialRenderer is initialized.
  • Hooks execute the parameter, if the parameter is a function.

Look at the code that occurs this error, the ReactPartialRenderer is already initialized to render <RichText/>. https://github.com/facebook/react/issues/16416#issue-481517156. But useMemo Hook calls processLink() -> renderToStaticMarkup() -> another ReactPartialRenderer is initialized. For now, those two ReactPartialRenderers are sharing the environment(closure) of ReactPartialRendererHooks.js

UseMemo - ReactFizzHooks.js

// workInProgressHook: {memoizedState : null}
const nextValue = nextCreate();
//this line calls renderToStaticMarkup() - another ReactPartialRenderer is created.

workInProgressHook.memoizedState = [nextValue, nextDeps]; // workInProgressHook is resetted. (become null)

That’s why this error has occurred.


There are some ideas to resolve this issue.

  1. Throw an error when the renderer is created more than one.
  2. Change ReactPartialRenderHooks into class, make each ReactPartialRenderer to have a ReactPartialRenderHooks instance.
  3. Create Weakmap using as a key for Component, a value for the environment. Using this environment, Remove reference of Weakmap when finishHooks() called.

@bvaughn @gaearon If you don’t mind, would you share your opinion on which one is the best? Is there a better way to handle this?

@jeremyscatigna This sounds right. Are you still working on this?