@mui/x-data-grid 8.29.0 → 8.29.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,8 +5,26 @@
5
5
  All notable changes to this project will be documented in this file.
6
6
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
7
7
 
8
+ ## 8.29.1
9
+
10
+ _Jun 11, 2026_
11
+
12
+ ### Data Grid
13
+
14
+ #### `@mui/x-data-grid@8.29.1`
15
+
16
+ - [DataGrid] Prevent React state update before mount (#22749) @arminmeh
17
+
18
+ #### `@mui/x-data-grid-pro@8.29.1` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan')
19
+
20
+ Same changes as in `@mui/x-data-grid@8.29.1`.
21
+
22
+ #### `@mui/x-data-grid-premium@8.29.1` [![premium](https://mui.com/r/x-premium-svg)](https://mui.com/r/x-premium-svg-link 'Premium plan')
23
+
24
+ Same changes as in `@mui/x-data-grid-pro@8.29.1`.
25
+
8
26
  ## 8.29.0
9
- <!-- generated comparing v8.28.7..v8.x -->
27
+
10
28
  _Jun 5, 2026_
11
29
 
12
30
  We'd like to extend a big thank you to the 2 contributors who made this release possible. Here are some highlights ✨:
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  'use client';
3
3
 
4
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
4
5
  var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default;
5
6
  Object.defineProperty(exports, "__esModule", {
6
7
  value: true
@@ -8,9 +9,9 @@ Object.defineProperty(exports, "__esModule", {
8
9
  exports.objectShallowCompare = exports.argsEqual = void 0;
9
10
  exports.useGridSelector = useGridSelector;
10
11
  var React = _interopRequireWildcard(require("react"));
12
+ var _useEnhancedEffect = _interopRequireDefault(require("@mui/utils/useEnhancedEffect"));
11
13
  var _fastObjectShallowCompare = require("@mui/x-internals/fastObjectShallowCompare");
12
14
  var _warning = require("@mui/x-internals/warning");
13
- var _shim = require("use-sync-external-store/shim");
14
15
  var _useLazyRef = require("./useLazyRef");
15
16
  const defaultCompare = Object.is;
16
17
  const objectShallowCompare = exports.objectShallowCompare = _fastObjectShallowCompare.fastObjectShallowCompare;
@@ -34,10 +35,10 @@ const createRefs = () => ({
34
35
  state: null,
35
36
  equals: null,
36
37
  selector: null,
37
- args: undefined
38
+ args: undefined,
39
+ storeState: null
38
40
  });
39
41
  const EMPTY = [];
40
- const emptyGetSnapshot = () => null;
41
42
  function useGridSelector(apiRef, selector, args = undefined, equals = defaultCompare) {
42
43
  if (!apiRef.current.state) {
43
44
  (0, _warning.warnOnce)(['MUI X: `useGridSelector` has been called before the initialization of the state.', 'This hook can only be used inside the context of the grid.']);
@@ -48,6 +49,9 @@ function useGridSelector(apiRef, selector, args = undefined, equals = defaultCom
48
49
  // We don't use an initialization function to avoid allocations
49
50
  didInit ? null : selector(apiRef, args));
50
51
  refs.current.state = state;
52
+ if (!didInit) {
53
+ refs.current.storeState = apiRef.current.store.state;
54
+ }
51
55
  refs.current.equals = equals;
52
56
  refs.current.selector = selector;
53
57
  const prevArgs = refs.current.args;
@@ -58,35 +62,34 @@ function useGridSelector(apiRef, selector, args = undefined, equals = defaultCom
58
62
  refs.current.state = newState;
59
63
  setState(newState);
60
64
  }
65
+ refs.current.storeState = apiRef.current.store.state;
61
66
  }
62
- const subscribe = React.useCallback(() => {
63
- if (refs.current.subscription) {
64
- return null;
65
- }
66
- refs.current.subscription = apiRef.current.store.subscribe(() => {
67
+ const updateState = React.useCallback(() => {
68
+ const storeState = apiRef.current.store.state;
69
+ if (refs.current.storeState !== storeState) {
67
70
  const newState = refs.current.selector(apiRef, refs.current.args);
71
+ refs.current.storeState = storeState;
68
72
  if (!refs.current.equals(refs.current.state, newState)) {
69
73
  refs.current.state = newState;
70
74
  setState(newState);
71
75
  }
72
- });
73
- return null;
76
+ }
74
77
  },
75
78
  // eslint-disable-next-line react-hooks/exhaustive-deps
76
79
  EMPTY);
77
- const unsubscribe = React.useCallback(() => {
78
- // Fixes issue in React Strict Mode, where getSnapshot is not called
79
- if (!refs.current.subscription) {
80
- subscribe();
81
- }
82
- return () => {
83
- if (refs.current.subscription) {
84
- refs.current.subscription();
85
- refs.current.subscription = undefined;
86
- }
87
- };
80
+
81
+ // Why subscribe in an effect instead of during render: a component can render without
82
+ // ever mounting (e.g. when it suspends during hydration). If it subscribed during render,
83
+ // it could receive a store update and call `setState` before being mounted (#17077).
84
+ // Effects only run for mounted components, so subscribing here is safe.
85
+ //
86
+ // Using a layout effect because the store may already have changed
87
+ // between render and mount (e.g. from a child's ref callback or layout effect).
88
+ // `updateState()` picks up such changes, so the corrected value is shown right away instead of in a second frame.
89
+ (0, _useEnhancedEffect.default)(() => {
90
+ updateState();
91
+ return apiRef.current.store.subscribe(updateState);
88
92
  // eslint-disable-next-line react-hooks/exhaustive-deps
89
93
  }, EMPTY);
90
- (0, _shim.useSyncExternalStore)(unsubscribe, subscribe, emptyGetSnapshot);
91
94
  return state;
92
95
  }
@@ -1,9 +1,9 @@
1
1
  'use client';
2
2
 
3
3
  import * as React from 'react';
4
+ import useEnhancedEffect from '@mui/utils/useEnhancedEffect';
4
5
  import { fastObjectShallowCompare } from '@mui/x-internals/fastObjectShallowCompare';
5
6
  import { warnOnce } from '@mui/x-internals/warning';
6
- import { useSyncExternalStore } from 'use-sync-external-store/shim';
7
7
  import { useLazyRef } from "./useLazyRef.mjs";
8
8
  const defaultCompare = Object.is;
9
9
  export const objectShallowCompare = fastObjectShallowCompare;
@@ -26,10 +26,10 @@ const createRefs = () => ({
26
26
  state: null,
27
27
  equals: null,
28
28
  selector: null,
29
- args: undefined
29
+ args: undefined,
30
+ storeState: null
30
31
  });
31
32
  const EMPTY = [];
32
- const emptyGetSnapshot = () => null;
33
33
  export function useGridSelector(apiRef, selector, args = undefined, equals = defaultCompare) {
34
34
  if (!apiRef.current.state) {
35
35
  warnOnce(['MUI X: `useGridSelector` has been called before the initialization of the state.', 'This hook can only be used inside the context of the grid.']);
@@ -40,6 +40,9 @@ export function useGridSelector(apiRef, selector, args = undefined, equals = def
40
40
  // We don't use an initialization function to avoid allocations
41
41
  didInit ? null : selector(apiRef, args));
42
42
  refs.current.state = state;
43
+ if (!didInit) {
44
+ refs.current.storeState = apiRef.current.store.state;
45
+ }
43
46
  refs.current.equals = equals;
44
47
  refs.current.selector = selector;
45
48
  const prevArgs = refs.current.args;
@@ -50,35 +53,34 @@ export function useGridSelector(apiRef, selector, args = undefined, equals = def
50
53
  refs.current.state = newState;
51
54
  setState(newState);
52
55
  }
56
+ refs.current.storeState = apiRef.current.store.state;
53
57
  }
54
- const subscribe = React.useCallback(() => {
55
- if (refs.current.subscription) {
56
- return null;
57
- }
58
- refs.current.subscription = apiRef.current.store.subscribe(() => {
58
+ const updateState = React.useCallback(() => {
59
+ const storeState = apiRef.current.store.state;
60
+ if (refs.current.storeState !== storeState) {
59
61
  const newState = refs.current.selector(apiRef, refs.current.args);
62
+ refs.current.storeState = storeState;
60
63
  if (!refs.current.equals(refs.current.state, newState)) {
61
64
  refs.current.state = newState;
62
65
  setState(newState);
63
66
  }
64
- });
65
- return null;
67
+ }
66
68
  },
67
69
  // eslint-disable-next-line react-hooks/exhaustive-deps
68
70
  EMPTY);
69
- const unsubscribe = React.useCallback(() => {
70
- // Fixes issue in React Strict Mode, where getSnapshot is not called
71
- if (!refs.current.subscription) {
72
- subscribe();
73
- }
74
- return () => {
75
- if (refs.current.subscription) {
76
- refs.current.subscription();
77
- refs.current.subscription = undefined;
78
- }
79
- };
71
+
72
+ // Why subscribe in an effect instead of during render: a component can render without
73
+ // ever mounting (e.g. when it suspends during hydration). If it subscribed during render,
74
+ // it could receive a store update and call `setState` before being mounted (#17077).
75
+ // Effects only run for mounted components, so subscribing here is safe.
76
+ //
77
+ // Using a layout effect because the store may already have changed
78
+ // between render and mount (e.g. from a child's ref callback or layout effect).
79
+ // `updateState()` picks up such changes, so the corrected value is shown right away instead of in a second frame.
80
+ useEnhancedEffect(() => {
81
+ updateState();
82
+ return apiRef.current.store.subscribe(updateState);
80
83
  // eslint-disable-next-line react-hooks/exhaustive-deps
81
84
  }, EMPTY);
82
- useSyncExternalStore(unsubscribe, subscribe, emptyGetSnapshot);
83
85
  return state;
84
86
  }
package/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @mui/x-data-grid v8.29.0
2
+ * @mui/x-data-grid v8.29.1
3
3
  *
4
4
  * @license MIT
5
5
  * This source code is licensed under the MIT license found in the
package/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @mui/x-data-grid v8.29.0
2
+ * @mui/x-data-grid v8.29.1
3
3
  *
4
4
  * @license MIT
5
5
  * This source code is licensed under the MIT license found in the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mui/x-data-grid",
3
- "version": "8.29.0",
3
+ "version": "8.29.1",
4
4
  "author": "MUI Team",
5
5
  "description": "The Community plan edition of the MUI X Data Grid components.",
6
6
  "license": "MIT",