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

Usage with expo #4

Open
GaspardC opened this issue Oct 7, 2020 · 28 comments
Open

Usage with expo #4

GaspardC opened this issue Oct 7, 2020 · 28 comments
Assignees

Comments

@GaspardC
Copy link

GaspardC commented Oct 7, 2020

Hi, is it meant to work with Expo ?

I'm trying with the last Expo (sdk 39) but the app is stuck on the Persist Gate which only display the loading component.

thanks

@roadmanfong
Copy link
Owner

Hi, could you create a basic repository so that I can investigate it?

@Trashpants
Copy link

Trashpants commented Oct 19, 2020

So I'm also getting this its very weird - when I run expo and remotely debug its totally fine, but when I disable remote debugging it stops working and refuses to load.


Will Attempt to set up a repo at some point today

@Trashpants
Copy link

So I've scratched up a https://github.com/Trashpants/zustand-demo-expo Hopefully this helps get to the bottom of this.

@alexandersandberg
Copy link
Contributor

I'm having the same problem without Expo.

@Trashpants
Copy link

@roadmanfong Anything we can do to help getting this resolved?

@callmetwan
Copy link
Contributor

callmetwan commented Nov 2, 2020

It appears that the white screen is actually a stuck loading screen. You can test by providing <PersistGate> with a loading prop.

<PersistGate
  loading={
    <View>
	    <BaseText>Loading</BaseText>
    </View>
}
>
// App content
</PersistGate>
);

I haven't dug further, I don't know that I have the time to.

@Trashpants
Copy link

Yeah it seems to not get past the loading, which points to the fact it’s not rehyrdating or not completing the rehydration process.

@callmetwan
Copy link
Contributor

callmetwan commented Nov 3, 2020

@Trashpants I think I got it to work. A key part of the hydration happening is calling the store within your main App structure. Below I have a partial from my App.tsx file and my store.ts. In store.ts I'm instantiating a zustand store utilizing configurePersist zustand-persist. Then in App.tsx I call the hook.

//store.ts
const { persist, purge } = configurePersist({
	storage: AsyncStorage,
	rootKey: 'root'
});

export const useStore = create(
	persist(
		{
			key: 'data'
		},
		set => ({
			count: 0,
			method: () => set((state: any) => ({ count: state.count + 1 }))
		})
	)
);


//App.tsx
const App: () => JSX.Element = () => {
	const { data } = useStore();
	return (
		<PersistGate
			loading={
				<View>
					<BaseText>Loading</BaseText>
				</View>
			}
		>
                     // App content
		</PersistGate>
	);
};

I was very confused when installing the library that no configuration parameters were passed to PersistGate. This answers why.

@Trashpants
Copy link

@callmetwan 's solution was exactly what was needed - making a call to some kind of state in the same screen as <PersistGate> is what was needed

For anyone looking at this or confused the magic line in @callmetwan solution is:

const { data } = useStore();

within App.tsx

@GaspardC GaspardC closed this as completed Nov 3, 2020
@callmetwan
Copy link
Contributor

@Trashpants While not strictly related to this topic it is worth noting that you cannot use any non-stringable types in your store if using this zustand-persist. The reason being that localStorage and AsyncStorage serialize their values (require them to be strings). This may or may not have practical limitations on how you build your store.

For instance, any actions you create must exist during bootstrapping. If you tried to add them using useStore.setState({newAction: () => console.log('hi')}) they will exist while the app is in memory but they will not be persisted. This also means you cannot use types like Map or Set.

I was already intending to use whichever state solution I decided on in the with AsyncStorage so I'm not losing much, but others reading should be aware. Just to be clear, this isn't a limitation of this library, this is a limitation of localStorage and AsyncStorage.

@Trashpants
Copy link

hmmm I've just got around to trying with iOS and again I'm hitting the thing I had before which is very weird - unless im debugging it doesn't want to get past loading - what's super strange is that I can update the store within the loading section:

/**
 * font loading stuff
 */

  const { onboardingComplete, toggleOnboardingComplete } = useSettingsStore();
  return (
    <PersistGate
      loading={
        <View
          style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}
        >
          <Text
            onPress={() => {
              toggleOnboardingComplete();
            }}
          >
            APP HAS LOADED: {onboardingComplete + ''}, fonts loaded:
            {loaded + ''},
          </Text>
        </View>
      }
    >
      {loaded && onboardingComplete && <RootNavigator />}
    </PersistGate>

@callmetwan
Copy link
Contributor

Very weird. For a moment that happened to me, but then I stopped/restarted the metro bundler and it all cleared up. I'm a bit busy today but I'd be interested in looking into this with you; I'm planning on using this library for a project and once I make a decision I won't have time to reverse it, so discovering and solving problems like these is important to me.

@alexandersandberg
Copy link
Contributor

const { data } = useStore();

within App.tsx

Hmm, this does nothing for me. I'm still having the same issue.

@Trashpants
Copy link

Out of interest, iOS or Android?

const { data } = useStore();
within App.tsx

Hmm, this does nothing for me. I'm still having the same issue.

what happens when you do remote debugging (assuming you're using expo)? Thats what's really leaving me scratching my head - when I have it enabled things work as expected (on iOS, android seems to be totally fine with and without remote debugging)

@alexandersandberg
Copy link
Contributor

iOS 14.1, simulator, not Expo.

PersistGate seems to open fine when debug mode is enabled.

@GaspardC GaspardC reopened this Nov 5, 2020
@Trashpants
Copy link

Anyone have any ideas where to start digging to fix this issue - I've been asked to use zustand in a project as other devs are comfortable using it, but I definitely need a persistence layer for RN.

especially as its impossible to actually console.log bits of data out for this anyone got any ideas of where to begin poking?

Obviously the isReady value inside the PersistGate file is always returning false, which makes sense but I'm struggling to get past that

@callmetwan
Copy link
Contributor

callmetwan commented Nov 9, 2020 via email

@Trashpants
Copy link

Trashpants commented Nov 9, 2020

okay so looking directly in the source code in node_modules:

index.js line 130

changing it from

return React__default['default'].createElement(React__default['default'].Fragment, null, isReady ? children : loading);

to

return React__default['default'].createElement(React__default['default'].Fragment, null, isReady ? children : loading(getLoadManager().onAllLoadedCallback()));

Gets me around it, which points me to thinking that the function just isn't being re-run as values change and firing it again (presumably after the data is updated) forces that re-render?

@Trashpants
Copy link

okay sorry to spam up the thread but it looks like the onAllLoaded function just flat out doesn't run. I'm not sure why it doesn't as the LoadManager.setLoaded function works as its possible to watch loadStatusRecord get updated.

Im not sure what happens but it appears that onAllLoaded is entirely failing to run or the method acts like its empty?

@callmetwan
Copy link
Contributor

Not spamming, this is helpful! I'll try to take a look at it tonight to see if I can add anything useful.

@Trashpants
Copy link

Trashpants commented Nov 9, 2020

I've done what you have suggested and copied the code into my project directly. Now the project never loads which seems weird but what I think is happening is as follows:

configurePersist creates a new LoadManager when trying to hydrate and once its complete it SHOULD then do an onAllLoadedCallback, however that's not set within the instance because.....

PersistGate is trying to get access to the SAME LoadManager instance where it should instantly set the onAllLoadedCallback. However that's not happening.

in LoadManager

update line 6 to be

this.onAllLoadedCallback = () => {
      console.log('INITIAL VERSION OF CALLBACK');
 };

And it will only be called once where from what I understand it should be called twice - technically once with the default callback and then once again once the onAllLoaded has been set within PersistGate

edit:

Ignore this comment im being a dope - made a typo in my app so it wasn't actually using the persist gate properly

@Trashpants
Copy link

Trashpants commented Nov 9, 2020

okay so, back to the original point I had, adding this inside the main body of PersistGate fixes the issue - ie forces it to run again.

(add it in at line 15)

  useEffect(() => {
    getLoadManager().setLoaded('');
  }, []);

the whole PeristGate file looks like this:

import React, { useEffect, useState } from 'react';

import { getLoadManager } from './LoadManager';

export interface PersistGateProps {
  children?: React.ReactNode;
  loading?: React.ReactNode;
  onBeforeLift?: () => void;
}

export function PersistGate(props: PersistGateProps): JSX.Element {
  const { children, loading = false, onBeforeLift } = props;
  const [isReady, setIsReady] = useState(false);

  useEffect(() => {
    getLoadManager().setLoaded('');
  }, []);

getLoadManager().onAllLoaded(() => {
    onBeforeLift && onBeforeLift();
    setIsReady(true);
  });

  return <React.Fragment>{isReady ? children : loading}</React.Fragment>;
}

I think this points to it being a race condition, I assume when we're debugging PeristGate loads first, then when the hydrate runs it fires the setLoaded up, when running without debugging I assume the other way around


edit:

Im pretty much stuck at this point, I know adding this in forces it to work for me. However it definitely feels like a total hack. @roadmanfong any ideas why re-running setLoaded that would get around the issue?

@saschageyer
Copy link

Hi,
is there any progress on this issue? We are also stuck at this point..

@ghacosta
Copy link

hi everybody! Do you have any news about this issue? I really want to use zustand but without Persist layer on React Native I'd go with redux and redux-persist.

@Trashpants
Copy link

As it currently stands I’m still running with that hack above and I’ve not come across any other issues with it.

has anyone else tried that out and seen if it solves the issue for them?

@smallsaucepan
Copy link
Collaborator

+1 for the useEffect workaround working for me. @roadmanfong would you be open to a PR even though this would be a workaround? Would make the library feel more predictable.

@roadmanfong
Copy link
Owner

Any pr are welcome here, I'm afraid I don't have time to implemented right away.

@smallsaucepan
Copy link
Collaborator

I think this might effectively be a duplicate of #9 as well. Will take a look at both and see what can be done.

@smallsaucepan smallsaucepan self-assigned this Nov 3, 2021
smallsaucepan added a commit to smallsaucepan/zustand-persist that referenced this issue Mar 11, 2022
smallsaucepan added a commit to smallsaucepan/zustand-persist that referenced this issue Mar 11, 2022
…stGate loading screens. Doesn't assume that PersistGate will be rendered before data is finished loading.
smallsaucepan added a commit to smallsaucepan/zustand-persist that referenced this issue Mar 12, 2022
…stGate loading screens. Doesn't assume that PersistGate will be rendered before data is finished loading.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants