0

I looked through all the posts with similar title but found nothing that helped me understand my issue and solve it.

I have created a context that passes down the state of a switch (toggled or not). Problem is consumers (children) are not receiving changed context value (which is set through a state). It's a primary value, a boolean, not an object or array so reconstruction is not necessary. I have no idea what I could be doing wrong.

const Price = ({ showYearlyPrice }) => {
  function getPriceParts(showYearlyPrice: boolean){
    return showYearlyPrice ? "a" : "b";
  }

  const mainPrice = getPriceParts(showYearlyPrice);

  return (
    <>
      <div className="flex flex-row">
      <p className="text-grey text-sm">
        {mainPrice}
      </p>
    </>
  );
};

const PricingHeader = ({
  price,
}) => {
  // Subscribe to switch event
  const isToggled = useContext(SwitchContext);
  console.log(isToggled)// Only prints once, with default value every time

  return (
      <SubscribableSwitch color={sectionBackground}>
        <Section bg={sectionBackground} spacing={SectionSpacing.BOTTOM_ONLY}>
           <Price showYearlyPrice={isToggled as boolean} price={price}/>
        </Section>
      </SubscribableSwitch>
  );
};

export default PricingHeader;

Then the actual SubscribableSwitch component, where the toggle works great and receives updated context value.

export const SwitchContext = createContext(false); // Default value, if I put "potato" that's what gets printed in children

const Toggle = ({ toggle }) => {
  const isToggled = useContext(SwitchContext);
  return (
    <div className="flex justify-center mb-8 mt-2">
      <h2 onClick={toggle}>click me</h2>
      {!isToggled && (
            <span>
              Not toggled
            </span>
          )}
    </div>
  );
};

const SubscribableSwitch = ({color, children}) => {
  const [isToggled, setToggle] = useState(false);
  const toggle = () => setToggle((value) => !value);

  return (
      <SwitchContext.Provider value={isToggled}>
        <Toggle toggle={toggle}/>
        {children} // Where children is PricingHeader
      </SwitchContext.Provider>
  );
};

export default SubscribableSwitch;
Chayemor
  • 3,577
  • 4
  • 31
  • 54

1 Answers1

0

I figured it out, and I feel so dumb about it.

useContext has to be used inside a child. A quote:

useContext(MyContext) only lets you read the context and subscribe to its changes. You still need a <MyContext.Provider> above in the tree to provide the value for this context.

The exact place where I went wrong is calling useContext in the render function of the component that prints SubscribableSwitch. Code should have been like this:

const Price = () => {
  function getPriceParts(showYearlyPrice: boolean){
    return showYearlyPrice ? "a" : "b";
  }

  const isToggled = useContext(SwitchContext); 
  const mainPrice = getPriceParts(isToggled);

  return (
    <>
      <div className="flex flex-row">
      <p className="text-grey text-sm">
        {mainPrice}
      </p>
    </>
  );
};

const PricingHeader = ({
  price,
}) => {
 //useContext should NOT be here, I'm not a child of the Context Provider
  return (
      <SubscribableSwitch color={sectionBackground}>
        <Section bg={sectionBackground} spacing={SectionSpacing.BOTTOM_ONLY}>
           <Price price={price}/>
        </Section>
      </SubscribableSwitch>
  );
};

export default PricingHeader;

Chayemor
  • 3,577
  • 4
  • 31
  • 54