aweary / react-copy-write

✍️ Immutable state with a mutable API
MIT License
1.79k stars 54 forks source link

Expose state outside of Consumer & mutate #55

Closed samhh closed 5 years ago

samhh commented 5 years ago

One thing I've often found myself wanting to do is access the store's state outside of React, or at least outside the scope of a consumer.

One option is mutate, however that places you into callback hell. My current preferred workaround is something like this:

const getState = () => new Promise((res) => {
  mutate((_, state) => {
    res(state);
  });
});

// ... inside an async function

const { myStoreProperty } = await getState();

However this isn't perfect either as it needlessly forces you into Promise-land.

Would it be possible to expose a function which just returns the current state - non-reactively of course - similar to the second argument of the mutate callback?

Alternatively, if I'm pursuing an anti-pattern with viable alternatives, I'd love to hear about that! :smile:

There does appear to be precedent for this in Redux.

Thanks!

aweary commented 5 years ago

mutate is synchronous, so you could avoid the Promise all together

const getState = () =>  {
  let _state = null;
  mutate((_, state) => {
    _state = state;
  });
  return _state;
}

but this is pretty messy and I wouldn't recommend it either 😄 I'm not so sure this is an anti-pattern yet, but my intuition is that it might be. I want react-copy-write to be a pretty small abstraction on top of React context. Since this is a problem that affects all context data (accessing outside of render) I'd prefer to stay consistent and then wait for the React team to come up with a generic solution that we can apply.

Passing the state down via props is my recommendation. I know it might require adding an additional wrapper component, but that's really not a huge cost in most cases.

samhh commented 5 years ago

Hmm, I tried that inside a larger function and was thrown some errors hence my assumption that it's asynchronous. I'll take another look at that at work on Monday.

Anyway, yeah, that's reasonable, best follow the React devs on this. I'll re-open this issue or open a new one if/when things change.

aweary commented 5 years ago

Hmm, I tried that inside a larger function and was thrown some errors hence my assumption that it's asynchronous.

Here's the source if you're curious. fn is the function that you pass to mutate

https://github.com/aweary/react-copy-write/blob/d7e1a51cc121753ea8ab7fca2ba8fd82a0c73c9c/src/index.js#L69-L77

samhh commented 5 years ago

@aweary Just for the sake of my own sanity, I tried the non-promise form of getState again and it always returns undefined, which would suggest that it is async?

apolzon commented 5 years ago

I was also looking for a way to access the provider's state outside of render. In my case, I needed it in a function that was defined in the same component that was defining the Provider wrapper.

To access the state, I stored a ref to the Provider component and accessed the state directly via this.providerRef.state. Is there anything wrong with accessing it this way, assuming I am not changing any values? Or is this a big code smell/anti-pattern? (i.e., is the best practice that my provider be defined up another level in a container component?)

Probably worth mentioning this is not a full react app (using react-rails and only parts of the app are currently rendered via jsx) so I don't have a full app wrapper where I can place a global Provider.