'rerender check - with same state of `useState` but not with same state of redux, why?

Here's the screensnapshot:

enter image description here

When I click 1,2, both won't cause rerender. When once I click 3, clicking 1 does not cause rerender, and only clicking 2 will cause rerender, what's the reason?

Note that

 const { key1, key2 } = useSelector(({ obj: { key1, key2 } }) => ({
    key1,
    key2
  }));

This is purposely put there to cause rerender should there is any rerender check since {key1,key2} will always be different (it's object).

Below are codes:

Here's the tryRerender.js, notice the comments inside for important information

import React, { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";

export default function TryRerender() {
  const dispatch = useDispatch();
  const [state, setState] = useState(1);

  //purposely put here to cause rerender
  const { key1, key2 } = useSelector(({ obj: { key1, key2 } }) => ({
    key1,
    key2
  }));

  //this is rerendering
  const setSameState = (e) => {
    console.log("same local state");
    setState((state) => state);
  };

  //this is not rerendering, why?
  const setSameRedux = (e) => {
    console.log("same redux state");
    dispatch({ type: "same" });
  };

  const setDiffRedux = (e) => {
    console.log("different redux state");
    dispatch({ type: "diff" });
  };

  return (
    <>
      {console.log("rerender")}
      <button onClick={setSameState}>Same local State</button>
      <br />

      <button onClick={setSameRedux}>Same Redux State</button>

      <br />

      <button onClick={setDiffRedux}>Different Redux State</button>
    </>
  );
}

here's the App.js, notice comments inside too

import React from "react";
import { Provider } from "react-redux";
import myStore from "./Redux";
import TryRerender from "./tryRerender";

function App() {
  return (
    <Provider store={myStore}>
      <TryRerender></TryRerender>
    </Provider>
  );
}
export default App;

Here's the Redux.js

import React from "react";
import { Provider } from "react-redux";
import myStore from "./Redux";
import TryRerender from "./tryRerender";

function App() {
  return (
    <Provider store={myStore}>
      <TryRerender></TryRerender>
    </Provider>
  );
}
export default App;

Here's the index.js

import { StrictMode } from "react";
import { createRoot } from "react-dom/client";

import App from "./App";

const rootElement = document.getElementById("root");
const root = createRoot(rootElement);

root.render(
  <StrictMode>
    <App />
  </StrictMode>
);

Try code at codesandbox

My hypothesis

  1. onSelect will be checked ONLY when there is different state from the point where onSelect is declared;
  2. setState will trigger a state check, if onSelect is on same state, it skips check; otherwise it will always trigger a check.
  3. dispatch same state will make onSelect skip check as well.

I delve into the source codes here could be the related ones:

react-redux/src/hooks/useSelector.ts

https://github.com/reduxjs/react-redux/blob/a2853bcf2e04e073ba2ac13e3bb48829edcc2b60/src/hooks/useSelector.ts#L34 https://github.com/reduxjs/react-redux/blob/a2853bcf2e04e073ba2ac13e3bb48829edcc2b60/src/hooks/useSelector.ts#L53

react-redux/src/utils/useSyncExternalStore.ts

https://github.com/reduxjs/react-redux/blob/791e00945558eb1586d719b51666493276c8d63d/src/utils/useSyncExternalStore.ts#L2

react/packages/use-sync-external-store/src/useSyncExternalStoreWithSelector.js

https://github.com/facebook/react/blob/6bce0355c3e4bf23c16e82317094230908ee7560/packages/use-sync-external-store/src/useSyncExternalStoreWithSelector.js#L75



Solution 1:[1]

I think the first issue here is that you're creating a Redux store inside of the <App> component. Don't do that - you should never create a Redux store inside of a component!

In particular, React will double-render components when inside of a <StrictMode> tag. That means that two stores would get created here, because <App> would render twice. So, it's very likely that components are creating an initial subscription to the first store, but then the dispatches would happen to the second store.

Try moving the store creation outside of <App> and see if that helps.

Solution 2:[2]

You solution is poor because it only allows for a specific 3-digit sequence - i.e., 0,0,7. Imagine what you would have to do if the sequence was changed.

The provided solution is broken. For example, if the input is [0, 2, 3, 0, 7, 1] it will return True.

Here's something that actually works:

def spy_game(nums):
    code = [0, 0, 7]
    for i in range(len(nums)-len(code)):
        if nums[i:i+len(code)] == code:
            return True
    return False

Solution 3:[3]

There is nothing wrong with different implementations, per se. Both algorithms loop through the list in O(n).

However, your code also works if the term '07' is in the code, as it looks at the index x twice: once in the first loop, and again at the start of the second loop. Use y in range(x + 1, len(nums)) instead.

The given solution uses the fact that the list contains only integers, so that if '007' is encountered, the list will have an 'x' in it still. It feels a bit weird to me, since I cant see a use case where having a list [0, 0, 7] and checking len(code) == 0 after the code.pop(0) wouldn't work.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 markerikson
Solution 2 Albert Winestein
Solution 3