Skip to content
This repository has been archived by the owner on Feb 14, 2023. It is now read-only.

Global state returned by useGlobal() does not subscribe to changes. #137

Closed
carvinlo opened this issue Dec 11, 2019 · 8 comments
Closed
Labels
bug Something isn't working as expected.

Comments

@carvinlo
Copy link

import { addReducer, setGlobal, useDispatch, useGlobal, getGlobal } from "reactn";

// Initialize the global state with the value 0.
setGlobal({ value: 0 });

// When the increment reducer is called, increment the global value by X.
addReducer("increment", (global, dispatch) => ({
  value: global.value + 1
}));

const Post = props => {
  const increment = useDispatch("increment");
  const [global] = useGlobal();

  return (
    <>
      <Button
        onClick={() => {
          increment();
          // increment(global.value); // it works
        }}
      >
        {global.value}
      </Button>
    </>
  );
};
@carvinlo
Copy link
Author

it works also:

import { addReducer, setGlobal, useDispatch, useGlobal } from "reactn";

addReducer("increment", (global, dispatch) => {
  console.log("increment", global.value);
  return {
    value: (global.value || 0) + 1
  };
});

const Post = props => {
  const increment = useDispatch("increment");
  const [value] = useGlobal("value");
  return (
    <>
      <Button
        onClick={() => {
          increment();
        }}
      >
        {value}
      </Button>
    </>
  );
};

@ASHFAQPATWARI
Copy link

@carvinlo I am facing the exact same problem. If i just subscribe to global state using const [global] = useGlobal(); then the component is not getting updated, no re-render happens.

However if I subscribe to a key using const [value] = useGlobal("value"); , component re-renders properly.

@CharlesStover is this the intended behaviour or are we missing something?

@quisido
Copy link
Collaborator

quisido commented Dec 16, 2019

I would be good to have a minimal reproduction on CodePen or something similar. I'd need to investigate if this is a problem with the reducer or a problem with useGlobal. Both are used in production code and are well unit tested; and I've not experienced an issue with the component not re-rendering.

I'd definitely like to solve it for you, but I don't see anything wrong with the code as I see it here, which is why a CodePen would be helpful.

@quisido quisido added the support A user is requesting support. label Dec 16, 2019
@ASHFAQPATWARI
Copy link

ASHFAQPATWARI commented Dec 29, 2019

@CharlesStover Here is a stackblitz showing the issue: https://react-n7i5si.stackblitz.io/

Clicking on button will only update the component which has subscribed to an extra property other the globalstate.

@quisido
Copy link
Collaborator

quisido commented Jan 10, 2020

Thank you for that @ASHFAQPATWARI . I'm not familiar with stackbliz. Is it possible to see the original source code, similar to how CodePen lets you see the code alongside its output?

@quisido quisido changed the title The global state has changed, but the component has not been updated Global state returned by useGlobal() does not subscribe to changes. Jan 10, 2020
@quisido
Copy link
Collaborator

quisido commented Jan 10, 2020

I found the source code for stackblitz. Thank you, that is a good find. https://stackblitz.com/edit/react-n7i5si/

@quisido quisido added bug Something isn't working as expected. and removed support A user is requesting support. labels Jan 10, 2020
@ASHFAQPATWARI
Copy link

@CharlesStover any update on this one?

@quisido
Copy link
Collaborator

quisido commented Jan 25, 2020

I think this is due to the asynchronous nature of your lifecycle methods. Change useEffect to useLayoutEffect when you are synchronizing states. useLayoutEffect will fire after render but before paint, which is what you want in this case. In the above Stackblitz example, you are changing local state, repainting, then changing global state (useEffect). What you want is to change local state, change global state (useLayoutEffect), then repaint.

As to why one component renders correctly and the other doesn't, I think that is just an implementation detail of React that should not be of concern. They both pass unit tests for re-rendering on state change. When dealing with proper lifecycle methods, this issue doesn't arise. When dealing with improper lifecycle methods, the resulting behavior need not be accounted for.

Hope this helps. Great find! Without further reasoning, I believe this behavior is a side effect of the wrong lifecycle hook and that useLayoutEffect is the correct lifecycle hook for the intended behavior of the application.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Something isn't working as expected.
Projects
None yet
Development

No branches or pull requests

3 participants