Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ReactiveVar doesn't cause hook to update if updated in a separate function #7779

Closed
madmaxlax opened this issue Mar 3, 2021 · 8 comments
Closed

Comments

@madmaxlax
Copy link

Hello!
I was working with reactiveVars, following along with https://youtu.be/xASrlg9rmR4?t=957
the issue may be that I, like the video, was creating a reactiveVar in the cache, rather than just on its own. But here is what I ran into:
I have my reactive var, and I made a function to update that reactive var (like the video demonstrates with delete-todo)

i have a button that, when clicked, calls that function to update the reactive var,
and i display the value of that var with useReactiveVar

however it doesn't update

Intended outcome:
when i call the addColor function, it should update the reactiveVar, and that should in turn cause a re-render because my page refers to the value changed by it

Actual outcome:
calling addColor does seem to update the var, but that doesn't trigger a re-render on the useReactiveVar hook

How to reproduce the issue:
https://codesandbox.io/s/use-reactive-var-no-update-nh75m?file=/src/App.tsx

cacheModel.ts

export const cacheMoney: InMemoryCache = new InMemoryCache({
 /// other cache settings
});
export const initialUserSettingsVar = cacheMoney.makeVar<UserSettings>(initialUserSettings);
export function addColor(userSettingsVar: ReactiveVar<UserSettings>) {
  return (newColor = 'green') => {
    //get latest value of the settings
    const currVal = userSettingsVar();
    //update the color
    currVal.favoriteColor = newColor;
    //set the new value
    userSettingsVar(currVal);
  };
}

MyComponent.tsx

export const QueryPage = () => {
  const otherSettingsData = useReactiveVar(initialUserSettingsVar);

return (
 <>
      <Button
        onClick={() => {
          addColor(initialUserSettingsVar)(); //why doesn't this work?
          // initialUserSettingsVar({ ...initialUserSettingsVar(), favoriteColor: 'blue' }); //this works
        }}
      >
        Add Color
      </Button>
<div>Fav Color: {otherSettingsData.favoriteColor} </div>
)

}

Versions
apollo-client "latest" (yarn lock says 3.3.11)

@madmaxlax
Copy link
Author

slight update,
I tried doing this with a regular makeVar rather than doing that on my cache object. still running into the same issue.

i added a nav link to the same page, and clicking that forces a re-render and the expected data is there

@benjamn
Copy link
Member

benjamn commented Mar 3, 2021

@madmaxlax The root value that you store in the reactive variable has to change for any notifications to happen, so this code

    const currVal = userSettingsVar();
    //update the color
    currVal.favoriteColor = newColor;
    //set the new value
    userSettingsVar(currVal);

needs to modify currVal somehow, rather than mutating its contents:

userSettingsVar({
  ...userSettingsVar(),
  favoriteColor: newColor,
});

@madmaxlax
Copy link
Author

ah yes, that makes sense. i transferred it poorly from the 'delete todo' example in the video.

thank you!

@madmaxlax
Copy link
Author

@benjamn
Copy link
Member

benjamn commented Mar 5, 2021

@madmaxlax Great question! Here's the PR that allowed makeVar to be used as a free function: #6512. In short, cache.makeVar is only useful for backwards compatibility, at this point.

@limekiln
Copy link

limekiln commented Mar 22, 2021

Sorry to resurrect this issue even though it was closed, but I have this issue as well although I made sure to change the root object (even by additionally using lodash's cloneDeep() function, just to be extra sure). It worked perfectly fine until version 3.3.6, but starting with 3.3.7, the update of the reactive variable does not trigger a rerender in a component using the useReactiveVar() hook :(
The reactive variable itself is correctly updated right after the mutation.

@madmaxlax
Copy link
Author

@benjamn btw, we've been using reactiveVars on our team for a few months now and they're AWESOME
way easier than redux and even easier than React Context. yall should be shouting about these on the rooftops. even for folks not even calling a GraphQL backend

@jviall
Copy link

jviall commented Nov 30, 2021

I too have the issue that @limekiln mentions. I have a reactive var that is encapsulated in a custom hook like this (simplified for the example):

const useSelectionVar = (): [Set<string>, (newSelection?: Set<string>) => void] => {
  const selection = useReactiveVar(selectionVar);

  const setSelection = useCallback((newSelection = new Set()): void => {
    selectionVar(newSelection);
  }, []);

  return [
    selection,
    setSelection,
  ];
};

I can see that calling setSelection properly updates the value of the reactive var (useEffects dependent on it re-run) but components that call this custom hook don't always re-render. I've found that it's particularly reproduceabe on a fairly fresh cache instance. In our usage of setSelection, we always pass a new Set.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Feb 15, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants