镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

SPFx 1.24 RC: on modern list pages an extension's hooks sometimes run against the list view host's React ("Cannot read properties of null (reading 'useContext')") #11068

Open

Description

Target SharePoint environment

SharePoint Online

What SharePoint Framework version are you using?

1.24.0-rc.0

Developer environment

macOS (build), Linux (CI); Node 26.11.1

Browser(s)

Chromium (Playwright 1.63, headless), Edge

Describe the bug / error

An application customizer (a footer rendered into PlaceholderName.Bottom) and list view command sets, all on React 18.3.1 as the framework's externals (react, react-dom), sometimes fail on modern list and library pages. In those cases a component's first hook throws:

TypeError: Cannot read properties of null (reading 'useContext')
    at b.useContext (https://res.public.onecdn.static.microsoft/files/sp-client/listview-host-assembly_1_nb-no_a4637af26e6668c1a61467340d1f7ddc.js:2:5541)
    at sn (https://public-cdn.sharepointonline.com/<tenant>.sharepoint.com/sites/appcatalog/ClientSideAssets/<id>/footer-application-customizer_<hash>.js:43:41896)
    at https://public-cdn.sharepointonline.com/<tenant>.sharepoint.com/sites/appcatalog/ClientSideAssets/<id>/footer-application-customizer_<hash>.js:43:627820
    at N2 (https://public-cdn.sharepointonline.com/<tenant>.sharepoint.com/sites/appcatalog/ClientSideAssets/<id>/footer-application-customizer_<hash>.js:43:629976)
    at fn (https://res.public.onecdn.static.microsoft/files/sp-client/react-dom-18-bundle_none_f300932fc4ce98358599.js:4:58405)
    at i (https://res.public.onecdn.static.microsoft/files/sp-client/react-dom-18-bundle_none_f300932fc4ce98358599.js:4:119247)
    ...

The component renders through the framework's react-dom-18-bundle, but useContext resolves to the React copy inside listview-host-assembly. That copy's dispatcher is null while react-dom-18-bundle renders, so the hook throws: two copies of React on one page.

The failure is intermittent. The same page and the same packages load correctly on most attempts, and fail on some, in a test suite that opens list views with a footer and command sets (document library, a custom list). On canvas pages and single web part app pages we have not seen it.

Steps to reproduce

  1. Make an SPFx 1.24.0-rc.0 application customizer whose component uses a hook (useContext, useState), rendered with createRoot from react-dom/client into a bottom placeholder.
  2. Deploy it tenant-wide, or to a site, and open a modern list view or document library page on that site.
  3. Reload the page several times. On some loads the footer fails with the error above, and the stack shows listview-host-assembly for the hook and react-dom-18-bundle for the render.

Expected behavior

The react external of an SPFx component resolves to the same React instance that the framework's react-dom uses, on list pages as on other pages, so hooks work on every load.

Activity

  1. m365-agent-toolkit-assistant commented on Oct 20, 2026

    @m365-agent-toolkit-assistant

    👋 Hi Remi Blom-Ohlsen (@Remi749),

    Thank you for reporting this issue. Here is an AI-generated initial response:

    Classification: Bug — the reported intermittent hook failure is inconsistent with supported React-based SPFx extensions, though the cause is not confirmed.

    • The stack trace appears consistent with useContext resolving from a different React instance than the one used by the renderer. The reporter observes this on list and library pages, not canvas pages.
    • SPFx compatibility guidance lists React 18.3.1 and Node v26 for 1.24; those reported versions are compatible. The 1.24 preview notes list no fix for this symptom.

    Upgrade check: 1.24.0-rc.0 is the latest preview listed in those notes; no fixing version is documented.

    What to do now:

    1. Confirm the built extension and its dependencies contain no bundled second React copy.
    2. Share a minimal extension, bundle configuration, and traces from successful and failed reloads for SPFx triage.

    Useful links:


    Note: AI-generated guidance may be incomplete or incorrect. Please verify suggestions before applying them.

  2. Remi749 commented on Oct 21, 2026

    @Remi749
    Author

    👋 Hi Remi Blom-Ohlsen (@Remi749),

    Thank you for reporting this issue. Here is an AI-generated initial response:

    Classification: Bug — the reported intermittent hook failure is inconsistent with supported React-based SPFx extensions, though the cause is not confirmed.

    • The stack trace appears consistent with useContext resolving from a different React instance than the one used by the renderer. The reporter observes this on list and library pages, not canvas pages.
    • SPFx compatibility guidance lists React 18.3.1 and Node v26 for 1.24; those reported versions are compatible. The 1.24 preview notes list no fix for this symptom.

    Upgrade check: 1.24.0-rc.0 is the latest preview listed in those notes; no fixing version is documented.

    What to do now:

    1. Confirm the built extension and its dependencies contain no bundled second React copy.
    2. Share a minimal extension, bundle configuration, and traces from successful and failed reloads for SPFx triage.

    Useful links:

    Note: AI-generated guidance may be incomplete or incorrect. Please verify suggestions before applying them.

    Regarding the two requests:

    No second copy of React in our bundle. The application customizer's AMD header lists react, react-dom and react-dom/client as externals:

    define(["@microsoft/sp-application-base","SharedResources","react","PortfolioExtensionsStrings","react-dom","SharedLibraryStrings","@microsoft/sp-core-library","react-dom/client","@microsoft/sp-lodash-subset","@microsoft/sp-loader"]
    
    • What the bundle holds of the react package: only react/cjs/react-jsx-runtime. It takes React's internals from the external react.
    • What it does not hold: React's core (useContext, resolveDispatcher) and react-dom (createRoot).
    • Where the failing call was: the useContext in the trace is in listview-host-assembly, not in our bundle. On that load, the module react resolved to the list view host's copy.

    Bundle configuration:

    • SPFx 1.24.0-rc.0 with Heft and the SPFx rig.
    • React 18.3.1 and react-dom 18.3.1.
    • config/config.json has "externals": {}.
    • Our one webpack change removes our own workspace packages (pp365-*) from externals, so they are bundled. It leaves react and react-dom external.

    The hook error still occurs, on document libraries.

    • A bug on our side, fixed. Our footer customizer subscribed to navigatedEvent only at the end of an asynchronous onInit. On list pages it usually missed the first event and rendered nothing. It now also renders at the end of onInit and on placeholderProvider.changedEvent.
    • On a list view on the hub: we then loaded a modern list view 28 times, each in a new tab of a signed-in browser. The hook error did not occur.
    • On a project's document library, it did. Our end-to-end run of 2026-10-21 opens our list view command set's dialog in a document library, right after the packages are deployed. The hook error occurred on 4 of 6 attempts.
      • The trace is the same as before: TypeError: Cannot read properties of null (reading 'useContext') at b.useContext (listview-host-assembly_1_nb-no_a4637af26e6668c1a61467340d1f7ddc.js:2:5541).
      • When it occurs, the dialog does not render.
    • Next. We can send the traces of a failing and a passing load from that run, and a minimal extension, if that helps.

    A related observation. On 7 of the 28 loads the footer still did not render, and nothing was logged as an error.

    • On the loads where the footer did not render: the Bottom placeholder element ([data-sp-placeholder="Bottom"]) was in the DOM, and our bundle was loaded. But the page never logged Defined clientSidePlaceholders: DialogContainer,Top,TopContent,Bottom, and changedEvent never fired. Two such loads were traced with console logging.
    • On the loads where it rendered: that line was logged shortly before or after our bundle arrived. Three such loads were traced with console logging.

    Should we open a separate issue for that?

  3. Ashlesha-MSFT commented on Oct 21, 2026

    @Ashlesha-MSFT
    Contributor

    We tested a minimal SPFx 1.24.0-rc.0 solution using React 18.3.1 as an external, with createRoot and hook-based components in both a Bottom-placeholder application customizer and a ListView Command Set dialog. We have not reproduced the exception in our test environment so far; both extension paths render successfully.

    This does not rule out the reported intermittent issue. Could you share the minimal extension and the sanitized passing/failing traces you offered, including the deployment-to-dialog test sequence and loaded framework script URLs? Please also confirm whether the command-set failure occurs during manual Edge testing or primarily in the Playwright run.

    We suggest tracking the missing-placeholder notification separately and cross-linking it, since that symptom can prevent rendering before React hooks execute.

  4. added
    Needs: Author FeedbackAwaiting response from the original poster of the issue. Marked as stale if no activity for 7 days.
    on Oct 21, 2026
  5. Remi749 commented on Oct 21, 2026

    @Remi749
    Author

    7. We tested a minimal SPFx 1.24.0-rc.0 solution using React 18.3.1 as an external, with createRoot and hook-based components in both a Bottom-placeholder application customizer and a ListView Command Set dialog. We have not reproduced the exception in our test environment so far; both extension paths render successfully.
    This does not rule out the reported intermittent issue. Could you share the minimal extension and the sanitized passing/failing traces you offered, including the deployment-to-dialog test sequence and loaded framework script URLs? Please also confirm whether the command-set failure occurs during manual Edge testing or primarily in the Playwright run.
    We suggest tracking the missing-placeholder notification separately and cross-linking it, since that symptom can prevent rendering before React hooks execute.

    Thanks for testing it. We have now reproduced it outside CI and traced it. The cause looks to be in how SharePoint's list view host registers SPFx's React module, not in the extensions.

    Summary

    With a cold browser cache, listview-host-assembly often runs twice on a list view. Each run registers SPFx's React module (0d910c1c-13b9-4e1c-9aa4-b008c5e42d7d_18.3.1).

    When react-dom-18-bundle registers between the two registrations, react-dom and our extensions hold different React instances. The extensions' first hook then throws Cannot read properties of null (reading 'useContext').

    How often it happens

    Each load below was the project's document library opened in a fresh browser context, the way a first visit or an InPrivate window loads it, unless the table says otherwise.

    Browser Cache What we did Loads Hook error
    Chromium (Playwright 1.63) cold opened the command set's dialog 10 7
    Microsoft Edge 141.0.3537.57 (Playwright, channel: 'msedge') cold opened the command set's dialog 6 3
    Chromium cold application customizer only 8 7
    Chromium warm: one profile, consecutive tabs opened the command set's dialog 8 1 (the 3rd load)
    Chromium cold a custom list on the hub site, application customizer only 6 1

    In the warm run, loads 5 to 8 bootstrapped through sp-loader-assembly instead, and passed.

    When it happens, both of our extensions on the page throw:

    • the application customizer, which renders into PlaceholderName.Bottom;
    • the ListView command set, whose dialog never renders.

    The trace

    1. The page's own <script> loads listview-host-assembly. On the loads that fail, it is fetched a second time, by script, with this initiator (from the DevTools protocol's Network.requestWillBeSent):

      n @ plt.odsp-common.js:31 < r @ plt.odsp-common.js:31 < (anonymous) @ splistwebpack.js:2
      
    2. Each run registers SPFx's React module. We logged every named define with a wrapper installed before the page's scripts. Every registration of 0d910c1c-13b9-4e1c-9aa4-b008c5e42d7d_18.3.1 came from:

      V.ensure  listview-host-assembly_1_nb-no_a4637af26e6668c1a61467340d1f7ddc.js:131:8742
      new K     listview-host-assembly_1_nb-no_a4637af26e6668c1a61467340d1f7ddc.js:131:5000
      ai.start  listview-host-assembly_1_nb-no_a4637af26e6668c1a61467340d1f7ddc.js:143:5730
      
    3. The RequireJS map (2.3.8) sends everyone to that id.

      • Our components map react to 0d910c1c-…_18.3.1, react-dom to aa0a46ec-1505-43cd-a44a-93f3a5aa460a_18.3.1 and react-dom/client to 3cadd7f5-5449-49c4-a153-1beb8facd90a_18.3.1.
      • aa0a46ec-… maps react to the same 0d910c1c-… id.
      • The React id has no paths entry: only the list view host defines it.
    4. The order decides it. Two loads, in milliseconds from navigation:

      failing:  React 1174 | react-dom 1212 | react-dom/client 1221 | React again 1263
      passing:  React 1558 | React again 1631 | react-dom 1667 | react-dom/client 1679
      

      We recorded the order on 30 loads:

      • All 15 where react-dom-18-bundle registered between the two React registrations threw.
      • None of the 15 with one registration, or both before react-dom, threw.
    5. The exception. The hook resolves to the list view host's React while react-dom renders:

      TypeError: Cannot read properties of null (reading 'useContext')
        at b.useContext (listview-host-assembly_1_nb-no_a4637af26e6668c1a61467340d1f7ddc.js:2:5541)
        at sn (footer-application-customizer_<hash>.js:43:41896)
        ...
        at fn (react-dom-18-bundle_none_f300932fc4ce98358599.js:4:58405)
      

      The command set's stack is the same, with template-selector-command-set_<hash>.js:43:10786 in place of the footer.

    Framework scripts loaded

    • https://res.public.onecdn.static.microsoft/files/sp-client/listview-host-assembly_1_nb-no_a4637af26e6668c1a61467340d1f7ddc.js: fetched twice on failing loads, as above.
    • https://res.public.onecdn.static.microsoft/files/sp-client/react-dom-18-bundle_none_f300932fc4ce98358599.js
    • https://res.public.onecdn.static.microsoft/files/sp-client/react-dom-client-18-bundle_none_67888f69ed394db31937.js
    • https://res.public.onecdn.static.microsoft/files/sp-client/odsp.react-18/odsp.react.lib-45e66721.js: the list view's own React. It is not the instance registered under the SPFx id.
    • splistwebpack build odsp-web-prod_2026-10-09.006: plt.odsp-common.js, plt.items-view.js and the others.
    • Our bundles: https://public-cdn.sharepointonline.com/<tenant>.sharepoint.com/sites/appcatalog/ClientSideAssets/<solution id>/footer-application-customizer_<hash>.js and …/template-selector-command-set_<hash>.js.
      • Both are requested by _configurePathResource in listview-host-assembly (line 130), together with the two react-dom bundles.

    Manual Edge testing or Playwright?

    • Manual use: we have not seen it. Our manual testing runs with a warm cache, where it is rare (see the table).
    • Playwright: the runs use fresh browser contexts. With real Edge 141 it failed on 3 of 6 cold loads.

    The deployment-to-dialog sequence

    • Not tied to a deployment. The loads above ran hours after the last deployment.
    • The CI sequence:
      1. The .sppkg files are added to the tenant app catalog, and the sites are upgraded.
      2. Each Playwright test opens the document library in a fresh browser context.
      3. The test clicks the command in the command bar and waits for the dialog.
    • Why CI meets it more often: the fresh contexts.

    To see it yourselves

    Open a document library with any hook-based extension in a cold profile, with this script installed before the page's scripts. It is a Playwright addInitScript, or DevTools with "Pause on first statement" in a fresh InPrivate window:

    let current
    Object.defineProperty(window, 'define', {
      configurable: true,
      get: () => current,
      set(fn) {
        current = typeof fn === 'function'
          ? Object.assign(function (...args) {
              if (/^(0d910c1c|aa0a46ec)/.test(args[0])) console.log('define', args[0], performance.now(), new Error().stack)
              return fn.apply(this, args)
            }, fn)
          : fn
      }
    })

    A failing load logs the React id, then aa0a46ec-…, then the React id again.

    Minimal extension

    Given the trace, we would expect your minimal extension to reproduce it too, on a document library with a cold cache. If it still does not, we are happy to package ours.

    The missing placeholder

    As suggested, it now has an issue of its own: #11070, cross-linked here. In short:

    • It happens on its own: on a warm load where the list view host ran once, and nothing threw, the page never logged Defined clientSidePlaceholders and changedEvent never fired.
    • The double run triggers it too: none of the 8 recorded loads where the host ran twice logged that line.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions