hookstate icon indicating copy to clipboard operation
hookstate copied to clipboard

Support useState "type" changes on component re-use

Open parisholley opened this issue 3 years ago • 3 comments

Use Case: I want to be able to have a component which can take either a boolean or a State, advantage being that I can defer/delegate listening to a value to the child component vs. re-rendering the parent to conditionally show a child.

This is an abbreviated reproduction (i believe, untested) of a component which includes Modal, but the props changed depending on the context. The error appears to be that inside the Modal instance, useState() will ALWAYS expect a State if it was initialized with one (and perhaps, vice versa). If I go from a State to a primitive boolean, I get the error:

index.js:1120 Uncaught TypeError: Cannot read property 'get' of undefined
    at useSubscribedStateMethods (index.js:1120)
    at useState (index.js:112)

Example:

function Modal({state}:{state:boolean | State<boolean>}){
  const scopedOpen = useState(open && typeof open !== 'boolean' ? open : false);

  return <></>;
}

function MyComponent({enable}:{enable: boolean}){
  const popup = useState(true);

  if(enable){
    return <Modal state={popup} />;
  }else{
    return <Modal state={false} />;
  }
}

To work around this, I have to use an explicit key on the Modals in order destroy the shared state:

function Modal({state}:{state:boolean | State<boolean>}){
  const scopedOpen = useState(open && typeof open !== 'boolean' ? open : false);

  return <></>;
}

function MyComponent({enable}:{enable: boolean}){
  const popup = useState(true);

  if(enable){
    return <Modal state={popup} key="state" />;
  }else{
    return <Modal state={false} key="primitive" />;
  }
}

I'd would be great if either:

A) We should a more helpful error message that this isn't possible B) We detect the change and replace the shared state, but perhaps this a smell?

parisholley avatar Jun 23 '21 13:06 parisholley

Hmm.. this is an interesting scenario and I am afraid it would be impossible to support it as the underlying "React.useState" would not support secondary data reinitialization. You are right, the key is the only way to force React to reset the state. So, (B) is not an option. (A) could be an option, but probably under development mode only as I am not sure how efficient it would be do this checking. I will leave the ticket open until I could find time to add this check. Or feel free to experiment and suggest a solution for check.

avkonst avatar Jun 23 '21 22:06 avkonst

This will become possible in Hookstate-4

avkonst avatar May 28 '22 01:05 avkonst

Hi, I have most of necessary bits to implement this corner case in Hookstate 4, but I need help for this one. Would you be able to write unit tests for this one? And I will try to match the tests spec in Hookstate code....

avkonst avatar Jun 20 '22 12:06 avkonst

Unfortunately this kind of use case is very hard to support. We decided to block it and throw an error when such a case is detected.

avkonst avatar Aug 06 '22 07:08 avkonst