1

Given a React component within a Next.js application that has a sub-section that can be set to something, or not, storing it in a variable like this:

export const MyComponent = (): JSX.Element => {
  let [result, setResult] = useState<ReactNode>()

  async function doFetch() {
    // ... await some API call
    setResult('Successful result!')
  }

  return (
    <div>
      <button onClick={doFetch}>Go</button>
      { result }
    </div>
  )
}

The ReactNode type allows for the variable to be a JSX element, a string, or null/undefined. However, the curly-brace insertion gets typed as Element, and I get an error that ReactNode cannot be assigned to an Element type, because undefined cannot be cast to a ReactElement<any, any> type.

The error is appearing in my Visual Studio IDE, and when doing a npx next build with the library dependencies of:

"@types/react": "18.0.20",
"@types/react-dom": "18.0.6",
"next": "12.3.1",
"react": "18.2.0",
"react-dom": "18.2.0"
"typescript": "4.8.3"

Is my type-setting on useState wrong? Or is something misconfigured that is forcing inline-expressions to be Element types (non-null) versus ReactNode types?

Answers on other React/Typescript questions like https://stackoverflow.com/a/72353143/144756 imply the type of an inline expression should be ReactNode, but my setup is declaring it as Element. Did something change with a recent version of React or Next?

MidnightLightning
  • 6,715
  • 5
  • 44
  • 68
  • What is the desired behavior? Do you want the compiler to error when doing this? – kelsny Sep 21 '22 at 15:24
  • Does this answer your question: https://stackoverflow.com/questions/58123398/when-to-use-jsx-element-vs-reactnode-vs-reactelement? – technophyle Sep 21 '22 at 15:26
  • @caTS The compiler is erroring (saying `result` needs to be an `Element`, and `ReactNode` doesn't cut it), but from how React works, I think it should be valid (it skips rendering null/undefined elements like that). The answer at https://stackoverflow.com/a/58123882/144756 indicates inline expressions like this should be `ReactNode` types, but `@types/react` 18.0.20 is making them `Element` instead? – MidnightLightning Sep 21 '22 at 16:00

1 Answers1

0

A possible solution is instead of using string or null options as part of the application flow, wrap them in React Fragments, such that they're always a JSX.Element, which makes Typescript happy:

  • null/undefined => <></>
  • String 'foo' => <>foo</>
export const MyComponent = (): JSX.Element => {
  let [result, setResult] = useState<JSX.Element>(<></>)

  async function doFetch() {
    // ... await some API call
    setResult(<>'Successful result!'</>)
  }

  return (
    <div>
      <button onClick={doFetch}>Go</button>
      { result }
    </div>
  )
}

This doesn't get at the root issue of why the curly-brace inline expression is being forced to be a JSX.Element type rather than ReactNode, and it seems like unnecessary boilerplate, but it does work around this error, if no other better solutions exist.

This workaround is suggested in this similar GitHub issue, and cites it cannot be fixed until this issue gets resolved in the upstream TypeScript library.

MidnightLightning
  • 6,715
  • 5
  • 44
  • 68