-
Notifications
You must be signed in to change notification settings - Fork 1
Context
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.
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 viacontext
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>
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 viacontext
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:
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;
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'sconfig
object andtheme
object
Using Cell Query Expressions
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.
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.