How to get props from another component in styled-components - styled-components

I'm trying to pass props to a child component's styled components so that I can dynamically set CSS styling. The below is what I tried, but it always goes false which is 50%. Seems like the props do not get passed down.
components/Col.js
const Col = styled.div`
flex: ${props => (props.size < 6 ? "0 0 100%" : "0 0 50%")};
max-width: ${props => (props.size < 6 ? "100%" : "50%")};
`;
export default ({ children }) => <Col>{children}</Col>;
pages/index.js
export default () => (
<App>
<Header />
<Row>
<Col size={3}>
<Card />
</Col>
</Row>
</App>
);

I put your code in a codesandbox and it functions as expected, there are 2 <Col />s, one which has a size of less than 6 and one greater, and the correct CSS rules are being applied based on props.
https://codesandbox.io/s/epicpoincaresd6ig-sd6ig
Are you sure it's not something in the <Card />'s CSS? Maybe you can post a sandbox link demonstrating your issue.

Related

useContext returning null upon useQuery

I am making a wrapper for the settings page in order for it to be reusable on every settings page. In it, there is a Context Provider in which I pass the user data, but it never seems to pass anything although the query is successful.
SettingsWrap.tsx
export const UserContext = React.createContext(null);
const SettingsWrap = ({ children }) => {
const [session] = useSession();
const { data, loading } = useQuery<SettingsData, SettingsVars>(
SETTINGS_QUERY,
{
variables: {
id: session?.id,
},
skip: !session,
}
);
return (
<Grid container spacing={2}>
<Grid item xs={12} sm={4}>
<SettingList />
</Grid>
{data && !loading ? (
<UserContext.Provider value={data.userId}>
<Grid
item
xs={12}
sm={8}
style={{ height: "100vh", overflow: "auto" }}
>
<Container>{children}</Container>
</Grid>
</UserContext.Provider>
) : (
<Grid
item
xs={12}
sm={8}
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
height: "100vh",
}}
>
<CircularProgress />
</Grid>
)}
</Grid>
);
};
AccountSettings.tsx
const user: UserInterface = useContext(UserContext); // THIS IS NULL
What is wrong with this code?
A context has a default value and can then be set to different other values by the use of providers, much like you have done. The value of a provider affects only the components in the children subtree of the component. Conversely, the current value of a context is the value set by the closest ancestor provider or, if none exists, the default value of the context.
Check the following example:
export const NumberContext = React.createContext(0); // default value
export default function App() {
return (
<div>
<Consumer /> // #1, shows 0
<NumberContext.Provider value={1}>
<Consumer /> // #2, shows 1
<NumberContext.Provider value={2}>
<Consumer /> // #3, shows 2
</NumberContext.Provider>
<div>
<div>
<Consumer /> // #4, shows 1
</div>
</div>
</NumberContext.Provider>
</div>
);
}
export function Consumer() {
const number = useContext(NumberContext)
return <p>{number}</p>
}
Here is a sandbox with this example: https://codesandbox.io/s/react-contexts-6dlh1
The first Consumer (#1) has no ancestor provider so it shows the default value of the context created which is 0.
The second Consumer (#2) is the direct child of a provider with value 1, and so shows value 1.
The third Consumer (#3) has two ancestor providers but the closest ancestor provider provides the value 2, so that's the value this component shows.
The fourth and last Consumer (#4) is nested under the first provider (since it lies outside the second provider but still inside the children hierarchy of the first provider). Thus it has an ancestor provider with the value 1 so it will also show value 1.
In your example, SettingsList (parent of AccountSettings), which is the presumed consumer of UserContext, does not have an ancestor provider (since it does not lie under the provider you have included in the component tree). Thus running useContext(UserContext) will always return null, the default value or UserContext, in that subtree of your component. Try putting SettingsList in a subtree with an ancestor provider and it will work.
Apparently, when I used useContext inside the Subcomponent where I passed "user" to, it worked perfectly fine. It was my mistake to use the context in the parent instead of the actual component.

CellMeasurer support of registerChild in react-virtualized

Using the render prop registerChild as ref in CellMeasurer seems to be ignored by react-virtualized: warning findDOMNode is deprecated still received.
That comment:
https://github.com/bvaughn/react-virtualized/issues/1353#issuecomment-569814307
makes me think that the fix is 4 month old, and latest version of react-virtualized is published 6 month ago
It is already mentioned in the doc though
https://github.com/bvaughn/react-virtualized/blob/master/docs/CellMeasurer.md#using-registerchild, but erroneously:
{({registerChild}) => (
<div
style={{
...style,
height: 35,
whiteSpace: 'nowrap'
}}
>
{content}
</div>
)}
the div seems to be missing the ref={registerChild} !
Anyway, with or without ref={registerChild} on the div,
warning findDOMNode is deprecated still received
The point of registerChild is to give the cellMeasurer a ref to the child component removing the need to call findDomNode. Currently the registerChild variable isn't being used, you need to add set the ref of the child component to registerChild.
{({registerChild}) => (
<div
ref={registerChild}
style={{
...style,
height: 35,
whiteSpace: 'nowrap'
}}
>
{content}
</div>
)}

Styling a child element in a third-party component

I'm using a 3rd party component named "Dialog" with the render method below. As you can see - the component has more than one className. I'd like to create a styled-component called StyledDialog which contains a prop that lets me override the width associated with the div that has the "SURFACE" className. Can this be done with Styled-Components - or do I need to bring the source code into my app and handle that manually.
render() {
const { className, children, onClose, open, ...otherProps } = this.props;
const ariaHiddenProp = open ? {} : { 'aria-hidden': true };
return (
<aside
className={classnames(
ROOT,
{
[ANIMATING]: this.state.animating,
[OPEN]: open,
},
className
)}
onClick={(e) => {
if (onClose) onClose(e);
}}
onTransitionEnd={() => {
this.setState({ animating: false });
}}
{...ariaHiddenProp}
>
<div
className={SURFACE}
onClick={(e) => {
e.stopPropagation();
}}
{...otherProps}
>
{children}
</div>
<div className={BACKDROP} />
</aside>
);
}
Based on your explanation, i think you should wrap this 3rd party component with styled method and apply your styles by referencing the corresponding classnames of that component from the wrapped styled component.
For instance, If the name of existing component is Hello, you can apply styling from a styled-component on any of its DOM children like this:
const StyledHello = styled(Hello)`
.${classes.SURFACE} {
width: 10rem;
border: 2px solid green;
}
`;
Working Demo

React Virtualized windowscroller scrollElement not working

You were right that I was not passing the props in correctly. Now I have it set up as such:
Container.jsx
<div className='container' ref={(ref) => {this.foo = ref;}}>
this.renderContainer()
</div>
<Section scrollContainer={this.foo}/>
Section.jsx (just passing down props)
<Panel scrollContainer={this.props.scrollContainer}/>
Section.propTypes = { scrollContainer: PropTypes.object.isRequired }
Panel.jsx (Passing down props)
<RenderedTable scrollContainer={this.props.scrollContainer} />
RenderedTable.jsx
return (
<div className='padding-top-20 font-smoothing'>
<WindowScroller scrollElement={this.props.scrollContainer}>
{({ height, isScrolling, scrollTop, onChildScroll }) => (
<AutoSizer disableHeight>
{({ width }) => (
<Table
Unfortunately the windowScroller still does not resize. I also dont get any warnings or errors. Do you use css tricks to get the scroller to resize? I see that in the example https://bvaughn.github.io/react-virtualized/#/components/WindowScroller
you change the flex and overflow properties when changing the scrollElement from window to scrollingBody.
I know you are very busy and greatly appreciate your help!
In your example, you've assigned the scroll ref to this.tabsContainer but you're trying to access it as this.props.tabContainer. Either this is your mistake, or the example is incomplete and more context is needed. :) Can you provide a Plnkr?

Styled Components - why does prop position affect styling?

I'm using styled-components to style a parent and child element in a component:
function StyledDemo({
name,
light,
...props
}) {
return (
<Parent {...props}>
<Child>{name}</Child>
</Parent>
);
}
I have a light prop which is true/false - but I'm having an issue with styling the elements based on the value of that property:
const Parent = styled.div`
background-color: #000;
width: 100%;
${props => props.light && `
background-color: #ccc;
`}
`;
The styling only seems to work when I remove the prop being passed into the function individually.
Parent element uses correct styling based on light prop value when:
function StyledDemo({ name, ...props })
Parent element does NOT use correct styling based on light prop value when:
function StyledDemo({ name, light, ...props })
I can get it all working by setting the prop on the Parent and Child component, but this doesn't seem like it's the best way:
return (
<Parent {...props} light={light}>
<Child light={light}>{name}</Child>
</Parent>
);
Is this the correct way to apply styles to components based on props, or is there an issue with my approach?
I have a demo to tinker with if it helps:
https://www.webpackbin.com/bins/-Kfcsujw99cjU7ttqgTz
This is not related to styled-components but to the rest parameter.
When you do the rest operator, any property you "pick" out by name won't be contained in the ...rest variable. So when you do
const Button = ({ light, ...rest }) => ()
<Button light primary />
rest will only contain the primary property, but not light, that's now it's own variable.
If you did
const Button = ({ ...rest }) => ()
<Button light primary />
instead rest would also contain light.
So in your example, you're picking out light from ...props, so when you pass {...props} on to the parent it doesn't contain light anymore, so styled-components doesn't know it exists! Either you go with your first version, or you have to manually apply it to each component.
See MDN for more information about the rest parameter!

Resources