CodeSandbox link
Just tested and it works like expected.
Component will not re-render if the return value does not change. Even if it's a deep nested value, like the example state.groupAB.propA = "valueA".
Of course, to test this behavior, you got to wrap it in React.memo(), otherwise it will re-render anyway whenever its parent re-renders.

App.js
import React from "react";
import "./styles.css";
import { useDispatch, useSelector } from "react-redux";
import SomeComponent from "./SomeComponent";
export default function App() {
console.log("App rendering...");
const state = useSelector((state) => state);
const dispatch = useDispatch();
function updateState() {
dispatch({ type: "UPLOAD_GROUP_AB_AND_PROP_B" });
}
return (
<div>
<div>State: {JSON.stringify(state)}</div>
<button onClick={updateState}>Update state</button>
<SomeComponent />
</div>
);
}
reducer.js
const initialState = {
groupAB: {
propA: "valueA",
propB: "valueB"
},
propC: "valueC"
};
export const reducer = (state = initialState, action) => {
switch (action.type) {
case "UPLOAD_GROUP_AB_AND_PROP_B": {
return {
// WILL CREATE A NEW STATE OBJ
...state,
groupAB: {
// WILL CREATE A NEW groupAB OBJ
...state.groupAB, // propA WILL REMAIN THE SAME
propB: "newValueB" // WILL UPDATE propB VALUE
}
};
}
default: {
return state;
}
}
};
SomeComponent.js
import React from "react";
const { useSelector } = require("react-redux");
function SomeComponent() {
console.log("SomeComponent rendering...");
const propA = useSelector((state) => state.groupAB.propA);
return (
<div>
<hr />
<div>This is SomeComponent</div>
state.groupAB.propA: {propA}
</div>
);
}
export default React.memo(SomeComponent);
index.js
import React from "react";
import ReactDOM from "react-dom";
import { createStore } from "redux";
import { Provider } from "react-redux";
import App from "./App";
import { reducer } from "./reducer";
const store = createStore(reducer);
const rootElement = document.getElementById("root");
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
rootElement
);