jamiebuilds / unstated-next

200 bytes to never think about React state management libraries ever again
MIT License
4.18k stars 145 forks source link

Can't compose containers manually? #81

Closed djMax closed 1 year ago

djMax commented 4 years ago

I have a react-native app and I'm just trying to simplify the code structure slightly by changing:

export default () => (
      <LocationContainer.Provider>
        <IdentityContainer.Provider>
          <FeatureContainer.Provider>
            <SessionContainer.Provider><App /></SessionContainer.Provider>
          </FeatureContainer.Provider>
        </IdentityContainer.Provider>
      </LocationContainer.Provider>
)

To something like:

export default withRootProviders(App)

However, I get the telltale error Error: Component must be wrapped with <Container.Provider>. I don't understand what's different about it - I've tried "simple" composition (with a children prop) as well as this HOC, both fail in the same way.

type PropsAreEqual<P> = (prevProps: Readonly<P>, nextProps: Readonly<P>) => boolean;

export default <P extends {}>(
  component: {
    (props: P): Exclude<React.ReactNode, undefined>;
    displayName?: string;
  },
  propsAreEqual?: PropsAreEqual<P> | false,
  componentName = component.displayName ?? component.name,
): {
  (props: P): JSX.Element;
  displayName: string;
} => {
  function WithRootProviders(props: P) {
    return (
      <LocationContainer.Provider>
        <IdentityContainer.Provider>
          <FeatureContainer.Provider>
            <SessionContainer.Provider>{component(props)}</SessionContainer.Provider>
          </FeatureContainer.Provider>
        </IdentityContainer.Provider>
      </LocationContainer.Provider>
    );
  }

  WithRootProviders.displayName = `withRootProviders(${componentName})`;

  let wrappedComponent =
    propsAreEqual === false ? WithRootProviders : React.memo(WithRootProviders, propsAreEqual);

  return wrappedComponent as typeof WithRootProviders;
};
olavoparno commented 4 years ago

maybe these would help

https://codesandbox.io/s/unstated-store-provider-k4h77?file=/src/store/index.js https://github.com/jamiebuilds/unstated-next#tip-1-composing-containers

djMax commented 4 years ago

I saw those, but that seems like it conflates the states into one big container, no?

olavoparno commented 4 years ago

I saw those, but that seems like it conflates the states into one big container, no?

yes, and it is left the responsibility to you to destruct what you need in order to handle component rerendering.

djMax commented 4 years ago

That would cause a bunch of over-rendering, no? Or is it going to happen anyways?

olavoparno commented 4 years ago

That would cause a bunch of over-rendering, no? Or is it going to happen anyways?

I don't remember for those examples but it might if you don't control it with useCallbacks, useMemos and memos as any other hook solution would probably. There are some solutions which come ready for this type of situations, I guess this isn't one of them.

djMax commented 4 years ago

I'm just confused what the difference is between the explicit and the wrapped versions I posted. It should just be adding an entry to the hierarchy, not altering it so fundamentally.