-
-
Notifications
You must be signed in to change notification settings - Fork 91
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
Fallback to local state when there's no Provider #75
Comments
So, in short, you're suggesting that, if there's no Provider, Constate should fallback to local state (as if That's an interesting idea and I'm open to it. I'd suggest an API like this: import React from "react";
import createContainer from "constate";
function useCount({ initialCount = 0 } = {}) {
const [count, setCount] = React.useState(initialCount);
const increment = () => setCount(prevCount => prevCount + 1);
return { count, increment };
}
// export a new useContainer method
// maybe deprecate Context
const { Context, Provider, useContainer } = createContainer(useCount);
function Component() {
// useContainer(fallbackProps)
// fallbackProps would be the hook props or just `true` to explicit say that it should fallback to local state.
// fallbackProps would be used only when there's no Provider
const { count, increment } = useContainer({ initialCount: 10 });
...
} This way I guess there will be no breaking changes, only an addition of Also, we can't name it just If the developer explicitly pass props to |
Excellent! Really great feedback! Much more elegant and better in every way (esp retaining the ability to pass props!). ... oh, and are you okay with the limitation about a container hook not being allowed to return Would you like to tackle this or can you wait awhile for a PR? (disclaimer: I'm pretty new at all this, so advanced apologies!) |
Yeah, this limitation isn't optimal since a hook could use only if (value === undefined) {
if (process.env.NODE_ENV !== "production") {
console.warn("[constate] Container hooks must not return `undefined`. `null` will be used instead. You can explicitly return `null` to disable this warning.");
}
value = null;
} Comparing to throwing the error, It would have less potential to break people's code, even though it's still a breaking change (one could expect context to return About tackling this. I'm really busy these days with another project, so feel free to work on this. :) |
An alternative solution could be using a static context just to tell that Provider exists: const HasProviderContext = React.createContext(false);
const Provider = props => {
const value = useValue();
// createMemoInputs won't change between renders
const memoizedValue = createMemoInputs
? React.useMemo(() => value, createMemoInputs(value))
: value;
return (
<Context.Provider value={memoizedValue}>
<HasProviderContext.Provider value={true}>
{props.children}
</HasProviderContext.Provider>
</Context.Provider>
);
};
const useContainer = fallbackProps => {
const hasProvider = React.useContext(HasProviderContext);
if (!hasProvider) {
...
}
} It looks hacky, but I guess that's totally valid use of context. And no breaking changes. |
Better (I guess): const NO_PROVIDER = "CONSTATE_NO_PROVIDER"
const Context = createContext<V>(NO_PROVIDER as V);
const useContainer = fallbackProps => {
const value = useContext(Context);
if (value === NO_PROVIDER) {
...
}
} |
Thanks. Had some time this afternoon so I've got a PR coming at you shortly. I ended up just checking:
Since really that seemed to the intent (and interestingly, if But feel free to noodle on this when you get the PR. I I also added a bunch of tests for the |
IMO adding for Ex:
|
If we're going to add this, it'll definitely not be for tests only. I can think of a few use cases where one would like to fallback to local state in production when using containers. But this can be done in user land by wrapping |
I can totally sympathize with the pushback. constate is wonderfully small and I agree bloating it would be bad. I'm totally fine w/ letting this thread die a nice peaceful death :-) Although wrapping the consumer in a component for testing is really tedious, I agree about the testing case, primarily because testing the behavior of container ( One final thought which might be worth considering before we close this topic (but again, totally fine to end the thread): The React team addressed this exact pattern when they made ...but I'm cool either way and appreciate the discussion (learned a lot!) |
Being hooks + context is exactly why Constate requires a Provider. Hooks can't be called from outside a component, so we can't use them to set a default value. We simply take the hook from the consumer component and lift it up to Provider. I updated the title to reflect better the enhancement I suggested. I still see value on this if this is a common need. But, after thinking better about it, right now I think it's not necessary as there are other easy work arounds. So I'm gonna close this for now. @ehahn9 Thank you for raising this discussion and taking the time to play with the code. |
I am needing this recently. I want to provide common components to my team that can be used with or without a provider. If the provider is there the components make use of it, otherwise you are required to pass in the props to each component that they need. |
@timkindberg You could do this already, so I guess the only annoying thing right now is the warning, right? |
@diegohaz that's right, I just want it to stop spamming the console. I'm just worried my coworkers will notice and make a stink about it. |
We can maybe have a |
We can also just remove the warning and see if people will open issues because they're forgetting the Provider. |
I mean, the regular context doesn't do a warning (pretty sure, because that's what I ended up doing). So if folks are expected to use the core lib properly... I think it's fine to remove it from this lib. |
Let's do that then. I think that would address #104 as well. |
Looking for a PR, or will you be able to do it? |
Thanks so much for
constate
- wonderful, indeed!I'm using it in a rather large project and finding that it is tedious to wrap every test with a
Provider
. React's use of a default context value increateContext
helps with this normally because thenuseContext
will get the default, even if no provider is in the tree. Alas, you can't do that withconstate
because there's no way use the hook outside of a component.I'd love to get your thoughts on this... What I'm thinking of is something like this (admittedly big) api change:
The
use()
function does theuseContext
subscription, but also knows how to call theunderlying hook instead if the context is empty. This makes writing tests really easy - just like React's
createContext
behavior when no provider is present.There are lots of (mostly unfortunate) issues with this proposal, tho:
Since we use
undefined
in the context to signify the no-provider case, container hooks can never returnundefined
(seems pretty minor?). Easy to guard for that case.Since
use()
has to call the hook when no provider is given, container hooks cannot haveprops anymore (seems pretty major).
It is a bit nasty to conditionally call the hook (
use
will call it if no provider is present, otherwise it won't). But this might not be a problem ITRW because you're either running real code (with a provider) or you're running tests (without a provider) and the render tree should be consistent across renders in each of those cases. React will complain if you get this wrong (like conditionally adding/removing providers, which seems super unlikely).So it ends up like something like this:
The text was updated successfully, but these errors were encountered: