Skip to content

Context

esr360 edited this page Feb 16, 2020 · 6 revisions

The context parameter is used to provide both state and props from parent Components/Modules. All parent state and props will be merged into this object at the top level, with the closest parent Component taking precedence. You can access state and props of specific parents via context.NAME, where NAME is the value of the name prop of the desired Component.

Accessing Parent Props

const styles = {
  fizz: ({ context }) => ({
    ...(context.foo && {
      // ...some styles
    }),
    ...(context.bar && {
      // ...some styles
    })
  }),

  buzz: ({ context }) => ({
    // ...if you want to be more specific:
    ...(context.myParent.foo && {
      // ...some styles
    }),
    ...(context.myParent.bar && {
      // ...some styles
    })
  })
}

const MyModule = (props) => (
  <Module styles={styles}>
    <Component name='myParent' foo={props.alpha} bar={props.beta}>
      <Component name='fizz'>Fizz</Component>
      <Component name='buzz'>Buzz</Component>
    </Component>
  </Module>
);
<MyModule alpha beta />

You can also pass props to <Module> and access them via context

Sass Equivalent

This concept is very similar to using the ampersand (&) in Sass, the difference is that instead of the context coming from parent class names, it comes from actual React context:

.fizz {
  .foo & {
    // ...some styles
  }

  .bar & {
    // ...some styles
  }
}
.buzz {
  // ...if you want to be more specific:
  .myParent.foo & {
    // ...some styles
  }

  .myParent.bar & {
    // ...some styles
  }
}
<div>
  <div class="myParent foo bar">
    <div class="fizz">Fizz</div>
    <div class="buzz">Buzz</div>
  </div>
</div>

Accessing Parent State

To access a parent's state, you should pass the state as a prop to the Component and access it through context:

const styles = {
  fizz: ({ context }) => ({
    ...(context.active && {
      // ...some styles
    })
  }),

  buzz: ({ context }) => ({
    // ...if you want to be more specific:
    ...(context.myParent.active && {
      // ...some styles
    })
  })
}

const MyModule = (props) => {
  const [isActive, setActive] = useState(false);

  return (
    <Module styles={styles} onClick={() => setActive(!isActive)}>
      <Component name='myParent' active={isActive}>
        <Component name='fizz'>Fizz</Component>
        <Component name='buzz'>Buzz</Component>
      </Component>
    </Module>
  );
}

You can also pass state as props to <Module> and access them via context

Accessing Context Object Within a Module's JSX

Rather than passing props down to your Module (prop-drilling) you can access all information about all parent Modules/Componets via the context object. To access this object within your Module's JSX, pass a function as the content when using <Module>, as seen in the following example:

Sample Context

Here we will wrap a <MyModule> instance with some other elements, utilising state and props

import AnotherModule from '../AnotherModule';
import MyModule from '../MyModule';

const MyContainer = (props) => {
  const [isPartyTime, setPartyTime] = useState(false);

  React.useEffect(() => {
    setTimeout(() => setPartyTime(true), 2000);
  });

  return (
    <AnotherModule partyTime={isPartyTime}>
      <MyModule>MyModule</MyModule>
    </AnotherModule>
  );
}

export default MyContainer;
Module With Render Props/Function as Child

Now within MyModule we can test for any context without having to manually pass it down [use with caution - in general it is probably best to explicitly pass down what you need]

const MyModule = (props) => (
  <Module styles={styles} {...props}>
    {({ context }) => {
      // some logic using `context`...
      console.log(context.AnotherModule);
      console.log(context.AnotherModule.partyTime);

      return props.children;
    }}
  </Module>
);

Note that using a render function with <Module> also exposes the Module's config object and theme object

You can avoid using functions to access context by using keywords in your styles object that correspond to Cell Query Expressions. The above initial example can be re-written as:

Prepend the Component's name with in-

const styles = {
  myComponent: {
    color: 'blue',

    'in-someParent': {
      color: 'red',
    }
  }
}

const MyModule = (props) => (
  <Module styles={styles} {...props}>
    <Component name='someParent'>
      <Component name='myComponent'>I will have red text</Component>
    </Component>

    <Component name='myComponent'>I will have blue text</Component>
  </Module>
);

This is useful for configuration, and using configuration to store cosmetic sylistic properties.

Accessing Parent's State

You can access the state/prop of the parent element by using the {COMPONENT}-is-{MODIFIER} CQ Expression (you can also target Modules and pseudo-states):

const styles = {
  myComponent: {
    'someParent-is-active': {
      // ...some styles
    }
  }
}

const MyModule = (props) => (
  <Module styles={styles} {...props}>
    <Component name='someParent' active={props.active}>
      <Component name='myComponent'>I will have red text</Component>
    </Component>

    <Component name='myComponent'>I will have blue text</Component>
  </Module>
);
<MyModule active />

If you need to apply some styles to myComponent with more advanced conditions relating to someParent, you can do:

const styles = {
  myComponent: {
    'in-someParent': {
      // ...some styles
      'and-is-active': {
        // ...some styles
      }
    }
  }
}

You can use this concept to an infinite depth, utilising other concepts such as :hover, to chain multiple contexts:

This example is part of a hypothetical accordion that would mask text content until its parent content Component is hovered (there would be better ways to do this, but its just for demo purposes)

const styles = {
  ...,

  text: {
    'in-panel': {
      'and-is-spoiler': {
        'background': 'grey'
      }
    }

    'in-content': {
      'and:hover': {
        'in-panel': {
          'and-is-spoiler': {
            'background': 'initial'
          }
        }
      }
    }
  }
}

const Accordion = ({ panels, toggle, spoiler, ...props }) => (
  <Module styles={styles} {...props}>
    {panels.map(({ title, content }) => {
      const [active, toggle] = useState(false);

      return (
        <Component name='panel' active={active} spoiler={props}>
          <Component name='title' onClick={() => toggle(!active)}>
            {title}
          </Component>
          <Component name='content'>
            <Component name='text'>{content}</Component>
          </Component>
        </Component>
      );
    })}
  </Module>
);
<MyModule spoiler />

Read the Cell Query page for more information.

Clone this wiki locally